linux/fs/cifs/cifs_debug.c
<<
>>
Prefs
   1/*
   2 *   fs/cifs_debug.c
   3 *
   4 *   Copyright (C) International Business Machines  Corp., 2000,2005
   5 *
   6 *   Modified by Steve French (sfrench@us.ibm.com)
   7 *
   8 *   This program is free software;  you can redistribute it and/or modify
   9 *   it under the terms of the GNU General Public License as published by
  10 *   the Free Software Foundation; either version 2 of the License, or
  11 *   (at your option) any later version.
  12 *
  13 *   This program is distributed in the hope that it will be useful,
  14 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
  15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  16 *   the GNU General Public License for more details.
  17 *
  18 *   You should have received a copy of the GNU General Public License
  19 *   along with this program;  if not, write to the Free Software
  20 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21 */
  22#include <linux/fs.h>
  23#include <linux/string.h>
  24#include <linux/ctype.h>
  25#include <linux/module.h>
  26#include <linux/proc_fs.h>
  27#include <linux/uaccess.h>
  28#include "cifspdu.h"
  29#include "cifsglob.h"
  30#include "cifsproto.h"
  31#include "cifs_debug.h"
  32#include "cifsfs.h"
  33
  34void
  35cifs_dump_mem(char *label, void *data, int length)
  36{
  37        pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data);
  38        print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4,
  39                       data, length, true);
  40}
  41
  42#ifdef CONFIG_CIFS_DEBUG
  43void cifs_vfs_err(const char *fmt, ...)
  44{
  45        struct va_format vaf;
  46        va_list args;
  47
  48        va_start(args, fmt);
  49
  50        vaf.fmt = fmt;
  51        vaf.va = &args;
  52
  53        pr_err_ratelimited("CIFS VFS: %pV", &vaf);
  54
  55        va_end(args);
  56}
  57#endif
  58
  59void cifs_dump_detail(void *buf)
  60{
  61#ifdef CONFIG_CIFS_DEBUG2
  62        struct smb_hdr *smb = (struct smb_hdr *)buf;
  63
  64        cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n",
  65                 smb->Command, smb->Status.CifsError,
  66                 smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
  67        cifs_dbg(VFS, "smb buf %p len %u\n", smb, smbCalcSize(smb));
  68#endif /* CONFIG_CIFS_DEBUG2 */
  69}
  70
  71void cifs_dump_mids(struct TCP_Server_Info *server)
  72{
  73#ifdef CONFIG_CIFS_DEBUG2
  74        struct list_head *tmp;
  75        struct mid_q_entry *mid_entry;
  76
  77        if (server == NULL)
  78                return;
  79
  80        cifs_dbg(VFS, "Dump pending requests:\n");
  81        spin_lock(&GlobalMid_Lock);
  82        list_for_each(tmp, &server->pending_mid_q) {
  83                mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
  84                cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
  85                         mid_entry->mid_state,
  86                         le16_to_cpu(mid_entry->command),
  87                         mid_entry->pid,
  88                         mid_entry->callback_data,
  89                         mid_entry->mid);
  90#ifdef CONFIG_CIFS_STATS2
  91                cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n",
  92                         mid_entry->large_buf,
  93                         mid_entry->resp_buf,
  94                         mid_entry->when_received,
  95                         jiffies);
  96#endif /* STATS2 */
  97                cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n",
  98                         mid_entry->multiRsp, mid_entry->multiEnd);
  99                if (mid_entry->resp_buf) {
 100                        cifs_dump_detail(mid_entry->resp_buf);
 101                        cifs_dump_mem("existing buf: ",
 102                                mid_entry->resp_buf, 62);
 103                }
 104        }
 105        spin_unlock(&GlobalMid_Lock);
 106#endif /* CONFIG_CIFS_DEBUG2 */
 107}
 108
 109#ifdef CONFIG_PROC_FS
 110static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 111{
 112        struct list_head *tmp1, *tmp2, *tmp3;
 113        struct mid_q_entry *mid_entry;
 114        struct TCP_Server_Info *server;
 115        struct cifs_ses *ses;
 116        struct cifs_tcon *tcon;
 117        int i, j;
 118        __u32 dev_type;
 119
 120        seq_puts(m,
 121                    "Display Internal CIFS Data Structures for Debugging\n"
 122                    "---------------------------------------------------\n");
 123        seq_printf(m, "CIFS Version %s\n", CIFS_VERSION);
 124        seq_printf(m, "Features:");
 125#ifdef CONFIG_CIFS_DFS_UPCALL
 126        seq_printf(m, " dfs");
 127#endif
 128#ifdef CONFIG_CIFS_FSCACHE
 129        seq_printf(m, " fscache");
 130#endif
 131#ifdef CONFIG_CIFS_WEAK_PW_HASH
 132        seq_printf(m, " lanman");
 133#endif
 134#ifdef CONFIG_CIFS_POSIX
 135        seq_printf(m, " posix");
 136#endif
 137#ifdef CONFIG_CIFS_UPCALL
 138        seq_printf(m, " spnego");
 139#endif
 140#ifdef CONFIG_CIFS_XATTR
 141        seq_printf(m, " xattr");
 142#endif
 143#ifdef CONFIG_CIFS_ACL
 144        seq_printf(m, " acl");
 145#endif
 146        seq_putc(m, '\n');
 147        seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
 148        seq_printf(m, "Servers:");
 149
 150        i = 0;
 151        spin_lock(&cifs_tcp_ses_lock);
 152        list_for_each(tmp1, &cifs_tcp_ses_list) {
 153                server = list_entry(tmp1, struct TCP_Server_Info,
 154                                    tcp_ses_list);
 155                seq_printf(m, "\nNumber of credits: %d", server->credits);
 156                i++;
 157                list_for_each(tmp2, &server->smb_ses_list) {
 158                        ses = list_entry(tmp2, struct cifs_ses,
 159                                         smb_ses_list);
 160                        if ((ses->serverDomain == NULL) ||
 161                                (ses->serverOS == NULL) ||
 162                                (ses->serverNOS == NULL)) {
 163                                seq_printf(m, "\n%d) Name: %s Uses: %d Capability: 0x%x\tSession Status: %d\t",
 164                                        i, ses->serverName, ses->ses_count,
 165                                        ses->capabilities, ses->status);
 166                                if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
 167                                        seq_printf(m, "Guest\t");
 168                                else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
 169                                        seq_printf(m, "Anonymous\t");
 170                        } else {
 171                                seq_printf(m,
 172                                    "\n%d) Name: %s  Domain: %s Uses: %d OS:"
 173                                    " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
 174                                    " session status: %d\t",
 175                                i, ses->serverName, ses->serverDomain,
 176                                ses->ses_count, ses->serverOS, ses->serverNOS,
 177                                ses->capabilities, ses->status);
 178                        }
 179                        seq_printf(m, "TCP status: %d\n\tLocal Users To "
 180                                   "Server: %d SecMode: 0x%x Req On Wire: %d",
 181                                   server->tcpStatus, server->srv_count,
 182                                   server->sec_mode, in_flight(server));
 183
 184#ifdef CONFIG_CIFS_STATS2
 185                        seq_printf(m, " In Send: %d In MaxReq Wait: %d",
 186                                atomic_read(&server->in_send),
 187                                atomic_read(&server->num_waiters));
 188#endif
 189
 190                        seq_puts(m, "\n\tShares:");
 191                        j = 0;
 192                        list_for_each(tmp3, &ses->tcon_list) {
 193                                tcon = list_entry(tmp3, struct cifs_tcon,
 194                                                  tcon_list);
 195                                ++j;
 196                                dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
 197                                seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
 198                                           tcon->treeName, tcon->tc_count);
 199                                if (tcon->nativeFileSystem) {
 200                                        seq_printf(m, "Type: %s ",
 201                                                   tcon->nativeFileSystem);
 202                                }
 203                                seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
 204                                        "\n\tPathComponentMax: %d Status: %d",
 205                                        le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
 206                                        le32_to_cpu(tcon->fsAttrInfo.Attributes),
 207                                        le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
 208                                        tcon->tidStatus);
 209                                if (dev_type == FILE_DEVICE_DISK)
 210                                        seq_puts(m, " type: DISK ");
 211                                else if (dev_type == FILE_DEVICE_CD_ROM)
 212                                        seq_puts(m, " type: CDROM ");
 213                                else
 214                                        seq_printf(m, " type: %d ", dev_type);
 215                                if (server->ops->dump_share_caps)
 216                                        server->ops->dump_share_caps(m, tcon);
 217
 218                                if (tcon->need_reconnect)
 219                                        seq_puts(m, "\tDISCONNECTED ");
 220                                seq_putc(m, '\n');
 221                        }
 222
 223                        seq_puts(m, "\n\tMIDs:\n");
 224
 225                        spin_lock(&GlobalMid_Lock);
 226                        list_for_each(tmp3, &server->pending_mid_q) {
 227                                mid_entry = list_entry(tmp3, struct mid_q_entry,
 228                                        qhead);
 229                                seq_printf(m, "\tState: %d com: %d pid:"
 230                                              " %d cbdata: %p mid %llu\n",
 231                                              mid_entry->mid_state,
 232                                              le16_to_cpu(mid_entry->command),
 233                                              mid_entry->pid,
 234                                              mid_entry->callback_data,
 235                                              mid_entry->mid);
 236                        }
 237                        spin_unlock(&GlobalMid_Lock);
 238                }
 239        }
 240        spin_unlock(&cifs_tcp_ses_lock);
 241        seq_putc(m, '\n');
 242
 243        /* BB add code to dump additional info such as TCP session info now */
 244        return 0;
 245}
 246
 247static int cifs_debug_data_proc_open(struct inode *inode, struct file *file)
 248{
 249        return single_open(file, cifs_debug_data_proc_show, NULL);
 250}
 251
 252static const struct file_operations cifs_debug_data_proc_fops = {
 253        .open           = cifs_debug_data_proc_open,
 254        .read           = seq_read,
 255        .llseek         = seq_lseek,
 256        .release        = single_release,
 257};
 258
 259#ifdef CONFIG_CIFS_STATS
 260static ssize_t cifs_stats_proc_write(struct file *file,
 261                const char __user *buffer, size_t count, loff_t *ppos)
 262{
 263        bool bv;
 264        int rc;
 265        struct list_head *tmp1, *tmp2, *tmp3;
 266        struct TCP_Server_Info *server;
 267        struct cifs_ses *ses;
 268        struct cifs_tcon *tcon;
 269
 270        rc = kstrtobool_from_user(buffer, count, &bv);
 271        if (rc == 0) {
 272#ifdef CONFIG_CIFS_STATS2
 273                atomic_set(&totBufAllocCount, 0);
 274                atomic_set(&totSmBufAllocCount, 0);
 275#endif /* CONFIG_CIFS_STATS2 */
 276                spin_lock(&cifs_tcp_ses_lock);
 277                list_for_each(tmp1, &cifs_tcp_ses_list) {
 278                        server = list_entry(tmp1, struct TCP_Server_Info,
 279                                            tcp_ses_list);
 280                        list_for_each(tmp2, &server->smb_ses_list) {
 281                                ses = list_entry(tmp2, struct cifs_ses,
 282                                                 smb_ses_list);
 283                                list_for_each(tmp3, &ses->tcon_list) {
 284                                        tcon = list_entry(tmp3,
 285                                                          struct cifs_tcon,
 286                                                          tcon_list);
 287                                        atomic_set(&tcon->num_smbs_sent, 0);
 288                                        if (server->ops->clear_stats)
 289                                                server->ops->clear_stats(tcon);
 290                                }
 291                        }
 292                }
 293                spin_unlock(&cifs_tcp_ses_lock);
 294        } else {
 295                return rc;
 296        }
 297
 298        return count;
 299}
 300
 301static int cifs_stats_proc_show(struct seq_file *m, void *v)
 302{
 303        int i;
 304        struct list_head *tmp1, *tmp2, *tmp3;
 305        struct TCP_Server_Info *server;
 306        struct cifs_ses *ses;
 307        struct cifs_tcon *tcon;
 308
 309        seq_printf(m,
 310                        "Resources in use\nCIFS Session: %d\n",
 311                        sesInfoAllocCount.counter);
 312        seq_printf(m, "Share (unique mount targets): %d\n",
 313                        tconInfoAllocCount.counter);
 314        seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n",
 315                        bufAllocCount.counter,
 316                        cifs_min_rcv + tcpSesAllocCount.counter);
 317        seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n",
 318                        smBufAllocCount.counter, cifs_min_small);
 319#ifdef CONFIG_CIFS_STATS2
 320        seq_printf(m, "Total Large %d Small %d Allocations\n",
 321                                atomic_read(&totBufAllocCount),
 322                                atomic_read(&totSmBufAllocCount));
 323#endif /* CONFIG_CIFS_STATS2 */
 324
 325        seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&midCount));
 326        seq_printf(m,
 327                "\n%d session %d share reconnects\n",
 328                tcpSesReconnectCount.counter, tconInfoReconnectCount.counter);
 329
 330        seq_printf(m,
 331                "Total vfs operations: %d maximum at one time: %d\n",
 332                GlobalCurrentXid, GlobalMaxActiveXid);
 333
 334        i = 0;
 335        spin_lock(&cifs_tcp_ses_lock);
 336        list_for_each(tmp1, &cifs_tcp_ses_list) {
 337                server = list_entry(tmp1, struct TCP_Server_Info,
 338                                    tcp_ses_list);
 339                list_for_each(tmp2, &server->smb_ses_list) {
 340                        ses = list_entry(tmp2, struct cifs_ses,
 341                                         smb_ses_list);
 342                        list_for_each(tmp3, &ses->tcon_list) {
 343                                tcon = list_entry(tmp3,
 344                                                  struct cifs_tcon,
 345                                                  tcon_list);
 346                                i++;
 347                                seq_printf(m, "\n%d) %s", i, tcon->treeName);
 348                                if (tcon->need_reconnect)
 349                                        seq_puts(m, "\tDISCONNECTED ");
 350                                seq_printf(m, "\nSMBs: %d",
 351                                           atomic_read(&tcon->num_smbs_sent));
 352                                if (server->ops->print_stats)
 353                                        server->ops->print_stats(m, tcon);
 354                        }
 355                }
 356        }
 357        spin_unlock(&cifs_tcp_ses_lock);
 358
 359        seq_putc(m, '\n');
 360        return 0;
 361}
 362
 363static int cifs_stats_proc_open(struct inode *inode, struct file *file)
 364{
 365        return single_open(file, cifs_stats_proc_show, NULL);
 366}
 367
 368static const struct file_operations cifs_stats_proc_fops = {
 369        .open           = cifs_stats_proc_open,
 370        .read           = seq_read,
 371        .llseek         = seq_lseek,
 372        .release        = single_release,
 373        .write          = cifs_stats_proc_write,
 374};
 375#endif /* STATS */
 376
 377static struct proc_dir_entry *proc_fs_cifs;
 378static const struct file_operations cifsFYI_proc_fops;
 379static const struct file_operations cifs_lookup_cache_proc_fops;
 380static const struct file_operations traceSMB_proc_fops;
 381static const struct file_operations cifs_security_flags_proc_fops;
 382static const struct file_operations cifs_linux_ext_proc_fops;
 383
 384void
 385cifs_proc_init(void)
 386{
 387        proc_fs_cifs = proc_mkdir("fs/cifs", NULL);
 388        if (proc_fs_cifs == NULL)
 389                return;
 390
 391        proc_create("DebugData", 0, proc_fs_cifs, &cifs_debug_data_proc_fops);
 392
 393#ifdef CONFIG_CIFS_STATS
 394        proc_create("Stats", 0, proc_fs_cifs, &cifs_stats_proc_fops);
 395#endif /* STATS */
 396        proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops);
 397        proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
 398        proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
 399                    &cifs_linux_ext_proc_fops);
 400        proc_create("SecurityFlags", 0, proc_fs_cifs,
 401                    &cifs_security_flags_proc_fops);
 402        proc_create("LookupCacheEnabled", 0, proc_fs_cifs,
 403                    &cifs_lookup_cache_proc_fops);
 404}
 405
 406void
 407cifs_proc_clean(void)
 408{
 409        if (proc_fs_cifs == NULL)
 410                return;
 411
 412        remove_proc_entry("DebugData", proc_fs_cifs);
 413        remove_proc_entry("cifsFYI", proc_fs_cifs);
 414        remove_proc_entry("traceSMB", proc_fs_cifs);
 415#ifdef CONFIG_CIFS_STATS
 416        remove_proc_entry("Stats", proc_fs_cifs);
 417#endif
 418        remove_proc_entry("SecurityFlags", proc_fs_cifs);
 419        remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
 420        remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
 421        remove_proc_entry("fs/cifs", NULL);
 422}
 423
 424static int cifsFYI_proc_show(struct seq_file *m, void *v)
 425{
 426        seq_printf(m, "%d\n", cifsFYI);
 427        return 0;
 428}
 429
 430static int cifsFYI_proc_open(struct inode *inode, struct file *file)
 431{
 432        return single_open(file, cifsFYI_proc_show, NULL);
 433}
 434
 435static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
 436                size_t count, loff_t *ppos)
 437{
 438        char c[2] = { '\0' };
 439        bool bv;
 440        int rc;
 441
 442        rc = get_user(c[0], buffer);
 443        if (rc)
 444                return rc;
 445        if (strtobool(c, &bv) == 0)
 446                cifsFYI = bv;
 447        else if ((c[0] > '1') && (c[0] <= '9'))
 448                cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */
 449
 450        return count;
 451}
 452
 453static const struct file_operations cifsFYI_proc_fops = {
 454        .open           = cifsFYI_proc_open,
 455        .read           = seq_read,
 456        .llseek         = seq_lseek,
 457        .release        = single_release,
 458        .write          = cifsFYI_proc_write,
 459};
 460
 461static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
 462{
 463        seq_printf(m, "%d\n", linuxExtEnabled);
 464        return 0;
 465}
 466
 467static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
 468{
 469        return single_open(file, cifs_linux_ext_proc_show, NULL);
 470}
 471
 472static ssize_t cifs_linux_ext_proc_write(struct file *file,
 473                const char __user *buffer, size_t count, loff_t *ppos)
 474{
 475        int rc;
 476
 477        rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled);
 478        if (rc)
 479                return rc;
 480
 481        return count;
 482}
 483
 484static const struct file_operations cifs_linux_ext_proc_fops = {
 485        .open           = cifs_linux_ext_proc_open,
 486        .read           = seq_read,
 487        .llseek         = seq_lseek,
 488        .release        = single_release,
 489        .write          = cifs_linux_ext_proc_write,
 490};
 491
 492static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
 493{
 494        seq_printf(m, "%d\n", lookupCacheEnabled);
 495        return 0;
 496}
 497
 498static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
 499{
 500        return single_open(file, cifs_lookup_cache_proc_show, NULL);
 501}
 502
 503static ssize_t cifs_lookup_cache_proc_write(struct file *file,
 504                const char __user *buffer, size_t count, loff_t *ppos)
 505{
 506        int rc;
 507
 508        rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled);
 509        if (rc)
 510                return rc;
 511
 512        return count;
 513}
 514
 515static const struct file_operations cifs_lookup_cache_proc_fops = {
 516        .open           = cifs_lookup_cache_proc_open,
 517        .read           = seq_read,
 518        .llseek         = seq_lseek,
 519        .release        = single_release,
 520        .write          = cifs_lookup_cache_proc_write,
 521};
 522
 523static int traceSMB_proc_show(struct seq_file *m, void *v)
 524{
 525        seq_printf(m, "%d\n", traceSMB);
 526        return 0;
 527}
 528
 529static int traceSMB_proc_open(struct inode *inode, struct file *file)
 530{
 531        return single_open(file, traceSMB_proc_show, NULL);
 532}
 533
 534static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
 535                size_t count, loff_t *ppos)
 536{
 537        int rc;
 538
 539        rc = kstrtobool_from_user(buffer, count, &traceSMB);
 540        if (rc)
 541                return rc;
 542
 543        return count;
 544}
 545
 546static const struct file_operations traceSMB_proc_fops = {
 547        .open           = traceSMB_proc_open,
 548        .read           = seq_read,
 549        .llseek         = seq_lseek,
 550        .release        = single_release,
 551        .write          = traceSMB_proc_write,
 552};
 553
 554static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
 555{
 556        seq_printf(m, "0x%x\n", global_secflags);
 557        return 0;
 558}
 559
 560static int cifs_security_flags_proc_open(struct inode *inode, struct file *file)
 561{
 562        return single_open(file, cifs_security_flags_proc_show, NULL);
 563}
 564
 565/*
 566 * Ensure that if someone sets a MUST flag, that we disable all other MAY
 567 * flags except for the ones corresponding to the given MUST flag. If there are
 568 * multiple MUST flags, then try to prefer more secure ones.
 569 */
 570static void
 571cifs_security_flags_handle_must_flags(unsigned int *flags)
 572{
 573        unsigned int signflags = *flags & CIFSSEC_MUST_SIGN;
 574
 575        if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
 576                *flags = CIFSSEC_MUST_KRB5;
 577        else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
 578                *flags = CIFSSEC_MUST_NTLMSSP;
 579        else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
 580                *flags = CIFSSEC_MUST_NTLMV2;
 581        else if ((*flags & CIFSSEC_MUST_NTLM) == CIFSSEC_MUST_NTLM)
 582                *flags = CIFSSEC_MUST_NTLM;
 583        else if (CIFSSEC_MUST_LANMAN &&
 584                 (*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN)
 585                *flags = CIFSSEC_MUST_LANMAN;
 586        else if (CIFSSEC_MUST_PLNTXT &&
 587                 (*flags & CIFSSEC_MUST_PLNTXT) == CIFSSEC_MUST_PLNTXT)
 588                *flags = CIFSSEC_MUST_PLNTXT;
 589
 590        *flags |= signflags;
 591}
 592
 593static ssize_t cifs_security_flags_proc_write(struct file *file,
 594                const char __user *buffer, size_t count, loff_t *ppos)
 595{
 596        int rc;
 597        unsigned int flags;
 598        char flags_string[12];
 599        bool bv;
 600
 601        if ((count < 1) || (count > 11))
 602                return -EINVAL;
 603
 604        memset(flags_string, 0, 12);
 605
 606        if (copy_from_user(flags_string, buffer, count))
 607                return -EFAULT;
 608
 609        if (count < 3) {
 610                /* single char or single char followed by null */
 611                if (strtobool(flags_string, &bv) == 0) {
 612                        global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF;
 613                        return count;
 614                } else if (!isdigit(flags_string[0])) {
 615                        cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
 616                                        flags_string);
 617                        return -EINVAL;
 618                }
 619        }
 620
 621        /* else we have a number */
 622        rc = kstrtouint(flags_string, 0, &flags);
 623        if (rc) {
 624                cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
 625                                flags_string);
 626                return rc;
 627        }
 628
 629        cifs_dbg(FYI, "sec flags 0x%x\n", flags);
 630
 631        if (flags == 0)  {
 632                cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string);
 633                return -EINVAL;
 634        }
 635
 636        if (flags & ~CIFSSEC_MASK) {
 637                cifs_dbg(VFS, "Unsupported security flags: 0x%x\n",
 638                         flags & ~CIFSSEC_MASK);
 639                return -EINVAL;
 640        }
 641
 642        cifs_security_flags_handle_must_flags(&flags);
 643
 644        /* flags look ok - update the global security flags for cifs module */
 645        global_secflags = flags;
 646        if (global_secflags & CIFSSEC_MUST_SIGN) {
 647                /* requiring signing implies signing is allowed */
 648                global_secflags |= CIFSSEC_MAY_SIGN;
 649                cifs_dbg(FYI, "packet signing now required\n");
 650        } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) {
 651                cifs_dbg(FYI, "packet signing disabled\n");
 652        }
 653        /* BB should we turn on MAY flags for other MUST options? */
 654        return count;
 655}
 656
 657static const struct file_operations cifs_security_flags_proc_fops = {
 658        .open           = cifs_security_flags_proc_open,
 659        .read           = seq_read,
 660        .llseek         = seq_lseek,
 661        .release        = single_release,
 662        .write          = cifs_security_flags_proc_write,
 663};
 664#else
 665inline void cifs_proc_init(void)
 666{
 667}
 668
 669inline void cifs_proc_clean(void)
 670{
 671}
 672#endif /* PROC_FS */
 673