Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 27 Jan 2008 07:01:20 +0000 (23:01 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 27 Jan 2008 07:01:20 +0000 (23:01 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  [CIFS] DFS build fixes
  [CIFS] DFS support: provide shrinkable mounts
  [CIFS] Do not log path names in lookup errors
  [CIFS] DFS support patchset: Added mountdata
  [CIFS] Forgot to add two new files from previous commit
  [CIFS] DNS name resolution helper upcall for cifs
  [CIFS] fix checkpatch warnings in fs/cifs/inode.c
  [CIFS] hold ses sem on tcp session reconnect during mount
  [CIFS] Allow setting mode via cifs acl
  [CIFS]  fix unicode string alignment in SPNEGO setup
  [CIFS] cifs_partialpagewrite() cleanup
  [CIFS]  use krb5 session key from first SMB session after a NegProt
  [CIFS] redo existing session setup if needed in cifs_mount
  [CIFS] Only dump SPNEGO key if CONFIG_CIFS_DEBUG2 is set
  [CIFS] fix SetEA failure to some Samba versions

23 files changed:
fs/Kconfig
fs/cifs/CHANGES
fs/cifs/Makefile
fs/cifs/README
fs/cifs/TODO
fs/cifs/cifs_dfs_ref.c [new file with mode: 0644]
fs/cifs/cifs_fs_sb.h
fs/cifs/cifs_spnego.c
fs/cifs/cifsacl.c
fs/cifs/cifsfs.c
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/dns_resolve.c [new file with mode: 0644]
fs/cifs/dns_resolve.h [new file with mode: 0644]
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/link.c
fs/cifs/sess.c

index b6df18f..9656139 100644 (file)
@@ -1899,13 +1899,15 @@ config CIFS
          file servers such as Windows 2000 (including Windows 2003, NT 4  
          and Windows XP) as well by Samba (which provides excellent CIFS
          server support for Linux and many other operating systems). Limited
-         support for OS/2 and Windows ME and similar servers is provided as well.
-
-         The intent of the cifs module is to provide an advanced
-         network file system client for mounting to CIFS compliant servers,
-         including support for dfs (hierarchical name space), secure per-user
-         session establishment, safe distributed caching (oplock), optional
-         packet signing, Unicode and other internationalization improvements. 
+         support for OS/2 and Windows ME and similar servers is provided as
+         well.
+
+         The cifs module provides an advanced network file system
+         client for mounting to CIFS compliant servers.  It includes
+         support for DFS (hierarchical name space), secure per-user
+         session establishment via Kerberos or NTLM or NTLMv2,
+         safe distributed caching (oplock), optional packet
+         signing, Unicode and other internationalization improvements.
          If you need to mount to Samba or Windows from this machine, say Y.
 
 config CIFS_STATS
@@ -1937,7 +1939,8 @@ config CIFS_WEAK_PW_HASH
          (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos)
          security mechanisms. These hash the password more securely
          than the mechanisms used in the older LANMAN version of the
-          SMB protocol needed to establish sessions with old SMB servers.
+         SMB protocol but LANMAN based authentication is needed to
+         establish sessions with some old SMB servers.
 
          Enabling this option allows the cifs module to mount to older
          LANMAN based servers such as OS/2 and Windows 95, but such
@@ -1945,8 +1948,8 @@ config CIFS_WEAK_PW_HASH
          security mechanisms if you are on a public network.  Unless you
          have a need to access old SMB servers (and are on a private 
          network) you probably want to say N.  Even if this support
-         is enabled in the kernel build, they will not be used
-         automatically. At runtime LANMAN mounts are disabled but
+         is enabled in the kernel build, LANMAN authentication will not be
+         used automatically. At runtime LANMAN mounts are disabled but
          can be set to required (or optional) either in
          /proc/fs/cifs (see fs/cifs/README for more detail) or via an
          option on the mount command. This support is disabled by 
@@ -2012,12 +2015,22 @@ config CIFS_UPCALL
          depends on CIFS_EXPERIMENTAL
          depends on KEYS
          help
-           Enables an upcall mechanism for CIFS which will be used to contact
-           userspace helper utilities to provide SPNEGO packaged Kerberos
-           tickets which are needed to mount to certain secure servers
+           Enables an upcall mechanism for CIFS which accesses
+           userspace helper utilities to provide SPNEGO packaged (RFC 4178)
+           Kerberos tickets which are needed to mount to certain secure servers
            (for which more secure Kerberos authentication is required). If
            unsure, say N.
 
+config CIFS_DFS_UPCALL
+         bool "DFS feature support (EXPERIMENTAL)"
+         depends on CIFS_EXPERIMENTAL
+         depends on KEYS
+         help
+           Enables an upcall mechanism for CIFS which contacts userspace
+           helper utilities to provide server name resolution (host names to
+           IP addresses) which is needed for implicit mounts of DFS junction
+           points. If unsure, say N.
+
 config NCP_FS
        tristate "NCP file system support (to mount NetWare volumes)"
        depends on IPX!=n || INET
index a609599..edd2483 100644 (file)
@@ -3,7 +3,10 @@ Version 1.52
 Fix oops on second mount to server when null auth is used.
 Enable experimental Kerberos support.  Return writebehind errors on flush
 and sync so that events like out of disk space get reported properly on
-cached files.
+cached files. Fix setxattr failure to certain Samba versions. Fix mount
+of second share to disconnected server session (autoreconnect on this).
+Add ability to modify cifs acls for handling chmod (when mounted with
+cifsacl flag).
 
 Version 1.51
 ------------
index 45e42fb..6ba43fb 100644 (file)
@@ -9,3 +9,5 @@ cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
          readdir.o ioctl.o sess.o export.o cifsacl.o
 
 cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
+
+cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
index bf11329..c623e2f 100644 (file)
@@ -56,7 +56,8 @@ the CIFS VFS web site) copy it to the same directory in which mount.smbfs and
 similar files reside (usually /sbin).  Although the helper software is not  
 required, mount.cifs is recommended.  Eventually the Samba 3.0 utility program 
 "net" may also be helpful since it may someday provide easier mount syntax for
-users who are used to Windows e.g.  net use <mount point> <UNC name or cifs URL>
+users who are used to Windows e.g.
+       net use <mount point> <UNC name or cifs URL>
 Note that running the Winbind pam/nss module (logon service) on all of your
 Linux clients is useful in mapping Uids and Gids consistently across the
 domain to the proper network user.  The mount.cifs mount helper can be
@@ -248,7 +249,7 @@ A partial list of the supported mount options follows:
                the CIFS session.
   password     The user password.  If the mount helper is
                installed, the user will be prompted for password
-               if it is not supplied.
+               if not supplied.
   ip           The ip address of the target server
   unc          The target server Universal Network Name (export) to 
                mount.  
@@ -283,7 +284,7 @@ A partial list of the supported mount options follows:
                can be enabled by specifying file_mode and dir_mode on 
                the client.  Note that the mount.cifs helper must be
                at version 1.10 or higher to support specifying the uid
-               (or gid) in non-numberic form.
+               (or gid) in non-numeric form.
   gid          Set the default gid for inodes (similar to above).
   file_mode     If CIFS Unix extensions are not supported by the server
                this overrides the default mode for file inodes.
@@ -417,9 +418,10 @@ A partial list of the supported mount options follows:
   acl          Allow setfacl and getfacl to manage posix ACLs if server
                supports them.  (default)
   noacl        Do not allow setfacl and getfacl calls on this mount
-  user_xattr    Allow getting and setting user xattrs as OS/2 EAs (extended
-               attributes) to the server (default) e.g. via setfattr 
-               and getfattr utilities. 
+  user_xattr    Allow getting and setting user xattrs (those attributes whose
+               name begins with "user." or "os2.") as OS/2 EAs (extended
+               attributes) to the server.  This allows support of the
+               setfattr and getfattr utilities. (default)
   nouser_xattr  Do not allow getfattr/setfattr to get/set/list xattrs 
   mapchars      Translate six of the seven reserved characters (not backslash)
                        *?<>|:
@@ -434,6 +436,7 @@ A partial list of the supported mount options follows:
  nomapchars     Do not translate any of these seven characters (default).
  nocase         Request case insensitive path name matching (case
                sensitive is the default if the server suports it).
+               (mount option "ignorecase" is identical to "nocase")
  posixpaths     If CIFS Unix extensions are supported, attempt to
                negotiate posix path name support which allows certain
                characters forbidden in typical CIFS filenames, without
@@ -485,6 +488,9 @@ A partial list of the supported mount options follows:
                        ntlmv2i Use NTLMv2 password hashing with packet signing
                        lanman  (if configured in kernel config) use older
                                lanman hash
+hard           Retry file operations if server is not responding
+soft           Limit retries to unresponsive servers (usually only
+               one retry) before returning an error.  (default)
 
 The mount.cifs mount helper also accepts a few mount options before -o
 including:
@@ -535,8 +541,8 @@ SecurityFlags               Flags which control security negotiation and
                        must use NTLM                                   0x02002
                        may use NTLMv2                                  0x00004
                        must use NTLMv2                                 0x04004
-                       may use Kerberos security (not implemented yet) 0x00008
-                       must use Kerberos (not implemented yet)         0x08008
+                       may use Kerberos security                       0x00008
+                       must use Kerberos                               0x08008
                        may use lanman (weak) password hash             0x00010
                        must use lanman password hash                   0x10010
                        may use plaintext passwords                     0x00020
@@ -626,6 +632,6 @@ returned success.
        
 Also note that "cat /proc/fs/cifs/DebugData" will display information about 
 the active sessions and the shares that are mounted.
-Enabling Kerberos (extended security) works when CONFIG_CIFS_EXPERIMENTAL is enabled
-but requires a user space helper (from the Samba project). NTLM and NTLMv2 and
-LANMAN support do not require this helpr.
+Enabling Kerberos (extended security) works when CONFIG_CIFS_EXPERIMENTAL is
+on but requires a user space helper (from the Samba project). NTLM and NTLMv2 and
+LANMAN support do not require this helper.
index a8852c2..92c9fea 100644 (file)
@@ -1,4 +1,4 @@
-Version 1.49 April 26, 2007
+Version 1.52 January 3, 2008
 
 A Partial List of Missing Features
 ==================================
@@ -16,16 +16,14 @@ SecurityDescriptors
 c) Better pam/winbind integration (e.g. to handle uid mapping
 better)
 
-d) Verify that Kerberos signing works
-
-e) Cleanup now unneeded SessSetup code in
+d) Cleanup now unneeded SessSetup code in
 fs/cifs/connect.c and add back in NTLMSSP code if any servers
 need it
 
-f) MD5-HMAC signing SMB PDUs when SPNEGO style SessionSetup 
-used (Kerberos or NTLMSSP). Signing alreadyimplemented for NTLM
-and raw NTLMSSP already. This is important when enabling
-extended security and mounting to Windows 2003 Servers
+e) ms-dfs and ms-dfs host name resolution cleanup
+
+f) fix NTLMv2 signing when two mounts with different users to same
+server.
 
 g) Directory entry caching relies on a 1 second timer, rather than 
 using FindNotify or equivalent.  - (started)
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
new file mode 100644 (file)
index 0000000..413ee23
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ *   Contains the CIFS DFS referral mounting routines used for handling
+ *   traversal via DFS junction point
+ *
+ *   Copyright (c) 2007 Igor Mammedov
+ *   Copyright (C) International Business Machines  Corp., 2008
+ *   Author(s): Igor Mammedov (niallain@gmail.com)
+ *             Steve French (sfrench@us.ibm.com)
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version
+ *   2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/dcache.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/vfs.h>
+#include <linux/fs.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifsfs.h"
+#include "dns_resolve.h"
+#include "cifs_debug.h"
+
+LIST_HEAD(cifs_dfs_automount_list);
+
+/*
+ * DFS functions
+*/
+
+void dfs_shrink_umount_helper(struct vfsmount *vfsmnt)
+{
+       mark_mounts_for_expiry(&cifs_dfs_automount_list);
+       mark_mounts_for_expiry(&cifs_dfs_automount_list);
+       shrink_submounts(vfsmnt, &cifs_dfs_automount_list);
+}
+
+/**
+ * cifs_get_share_name -       extracts share name from UNC
+ * @node_name: pointer to UNC string
+ *
+ * Extracts sharename form full UNC.
+ * i.e. strips from UNC trailing path that is not part of share
+ * name and fixup missing '\' in the begining of DFS node refferal
+ * if neccessary.
+ * Returns pointer to share name on success or NULL on error.
+ * Caller is responsible for freeing returned string.
+ */
+static char *cifs_get_share_name(const char *node_name)
+{
+       int len;
+       char *UNC;
+       char *pSep;
+
+       len = strlen(node_name);
+       UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */,
+                        GFP_KERNEL);
+       if (!UNC)
+               return NULL;
+
+       /* get share name and server name */
+       if (node_name[1] != '\\') {
+               UNC[0] = '\\';
+               strncpy(UNC+1, node_name, len);
+               len++;
+               UNC[len] = 0;
+       } else {
+               strncpy(UNC, node_name, len);
+               UNC[len] = 0;
+       }
+
+       /* find server name end */
+       pSep = memchr(UNC+2, '\\', len-2);
+       if (!pSep) {
+               cERROR(1, ("%s: no server name end in node name: %s",
+                       __FUNCTION__, node_name));
+               kfree(UNC);
+               return NULL;
+       }
+
+       /* find sharename end */
+       pSep++;
+       pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC));
+       if (!pSep) {
+               cERROR(1, ("%s:2 cant find share name in node name: %s",
+                       __FUNCTION__, node_name));
+               kfree(UNC);
+               return NULL;
+       }
+       /* trim path up to sharename end
+        *          * now we have share name in UNC */
+       *pSep = 0;
+
+       return UNC;
+}
+
+
+/**
+ * compose_mount_options       -       creates mount options for refferral
+ * @sb_mountdata:      parent/root DFS mount options (template)
+ * @ref_unc:           refferral server UNC
+ * @devname:           pointer for saving device name
+ *
+ * creates mount options for submount based on template options sb_mountdata
+ * and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
+ *
+ * Returns: pointer to new mount options or ERR_PTR.
+ * Caller is responcible for freeing retunrned value if it is not error.
+ */
+static char *compose_mount_options(const char *sb_mountdata,
+                                  const char *ref_unc,
+                                  char **devname)
+{
+       int rc;
+       char *mountdata;
+       int md_len;
+       char *tkn_e;
+       char *srvIP = NULL;
+       char sep = ',';
+       int off, noff;
+
+       if (sb_mountdata == NULL)
+               return ERR_PTR(-EINVAL);
+
+       *devname = cifs_get_share_name(ref_unc);
+       rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
+       if (rc != 0) {
+               cERROR(1, ("%s: Failed to resolve server part of %s to IP",
+                         __FUNCTION__, *devname));
+               mountdata = ERR_PTR(rc);
+               goto compose_mount_options_out;
+       }
+       md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref_unc) + 3;
+       mountdata = kzalloc(md_len+1, GFP_KERNEL);
+       if (mountdata == NULL) {
+               mountdata = ERR_PTR(-ENOMEM);
+               goto compose_mount_options_out;
+       }
+
+       /* copy all options except of unc,ip,prefixpath */
+       off = 0;
+       if (strncmp(sb_mountdata, "sep=", 4) == 0) {
+                       sep = sb_mountdata[4];
+                       strncpy(mountdata, sb_mountdata, 5);
+                       off += 5;
+       }
+       while ((tkn_e = strchr(sb_mountdata+off, sep))) {
+               noff = (tkn_e - (sb_mountdata+off)) + 1;
+               if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) {
+                       off += noff;
+                       continue;
+               }
+               if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) {
+                       off += noff;
+                       continue;
+               }
+               if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) {
+                       off += noff;
+                       continue;
+               }
+               strncat(mountdata, sb_mountdata+off, noff);
+               off += noff;
+       }
+       strcat(mountdata, sb_mountdata+off);
+       mountdata[md_len] = '\0';
+
+       /* copy new IP and ref share name */
+       strcat(mountdata, ",ip=");
+       strcat(mountdata, srvIP);
+       strcat(mountdata, ",unc=");
+       strcat(mountdata, *devname);
+
+       /* find & copy prefixpath */
+       tkn_e = strchr(ref_unc+2, '\\');
+       if (tkn_e) {
+               tkn_e = strchr(tkn_e+1, '\\');
+               if (tkn_e) {
+                       strcat(mountdata, ",prefixpath=");
+                       strcat(mountdata, tkn_e);
+               }
+       }
+
+       /*cFYI(1,("%s: parent mountdata: %s", __FUNCTION__,sb_mountdata));*/
+       /*cFYI(1, ("%s: submount mountdata: %s", __FUNCTION__, mountdata ));*/
+
+compose_mount_options_out:
+       kfree(srvIP);
+       return mountdata;
+}
+
+
+static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,
+               struct dentry *dentry, char *ref_unc)
+{
+       struct cifs_sb_info *cifs_sb;
+       struct vfsmount *mnt;
+       char *mountdata;
+       char *devname = NULL;
+
+       cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
+       mountdata = compose_mount_options(cifs_sb->mountdata,
+                                               ref_unc, &devname);
+
+       if (IS_ERR(mountdata))
+               return (struct vfsmount *)mountdata;
+
+       mnt = vfs_kern_mount(&cifs_fs_type, 0, devname, mountdata);
+       kfree(mountdata);
+       kfree(devname);
+       return mnt;
+
+}
+
+static char *build_full_dfs_path_from_dentry(struct dentry *dentry)
+{
+       char *full_path = NULL;
+       char *search_path;
+       char *tmp_path;
+       size_t l_max_len;
+       struct cifs_sb_info *cifs_sb;
+
+       if (dentry->d_inode == NULL)
+               return NULL;
+
+       cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
+
+       if (cifs_sb->tcon == NULL)
+               return NULL;
+
+       search_path = build_path_from_dentry(dentry);
+       if (search_path == NULL)
+               return NULL;
+
+       if (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS) {
+               /* we should use full path name to correct working with DFS */
+               l_max_len = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE+1) +
+                                       strnlen(search_path, MAX_PATHCONF) + 1;
+               tmp_path = kmalloc(l_max_len, GFP_KERNEL);
+               if (tmp_path == NULL) {
+                       kfree(search_path);
+                       return NULL;
+               }
+               strncpy(tmp_path, cifs_sb->tcon->treeName, l_max_len);
+               strcat(tmp_path, search_path);
+               tmp_path[l_max_len-1] = 0;
+               full_path = tmp_path;
+               kfree(search_path);
+       } else {
+               full_path = search_path;
+       }
+       return full_path;
+}
+
+static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd,
+                               struct list_head *mntlist)
+{
+       /* stolen from afs code */
+       int err;
+
+       mntget(newmnt);
+       err = do_add_mount(newmnt, nd, nd->mnt->mnt_flags, mntlist);
+       switch (err) {
+       case 0:
+               dput(nd->dentry);
+               mntput(nd->mnt);
+               nd->mnt = newmnt;
+               nd->dentry = dget(newmnt->mnt_root);
+               break;
+       case -EBUSY:
+               /* someone else made a mount here whilst we were busy */
+               while (d_mountpoint(nd->dentry) &&
+                      follow_down(&nd->mnt, &nd->dentry))
+                       ;
+               err = 0;
+       default:
+               mntput(newmnt);
+               break;
+       }
+       return err;
+}
+
+static void dump_referral(const struct dfs_info3_param *ref)
+{
+       cFYI(1, ("DFS: ref path: %s", ref->path_name));
+       cFYI(1, ("DFS: node path: %s", ref->node_name));
+       cFYI(1, ("DFS: fl: %hd, srv_type: %hd", ref->flags, ref->server_type));
+       cFYI(1, ("DFS: ref_flags: %hd, path_consumed: %hd", ref->ref_flag,
+                               ref->PathConsumed));
+}
+
+
+static void*
+cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
+{
+       struct dfs_info3_param *referrals = NULL;
+       unsigned int num_referrals = 0;
+       struct cifs_sb_info *cifs_sb;
+       struct cifsSesInfo *ses;
+       char *full_path = NULL;
+       int xid, i;
+       int rc = 0;
+       struct vfsmount *mnt = ERR_PTR(-ENOENT);
+
+       cFYI(1, ("in %s", __FUNCTION__));
+       BUG_ON(IS_ROOT(dentry));
+
+       xid = GetXid();
+
+       dput(nd->dentry);
+       nd->dentry = dget(dentry);
+
+       cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
+       ses = cifs_sb->tcon->ses;
+
+       if (!ses) {
+               rc = -EINVAL;
+               goto out_err;
+       }
+
+       full_path = build_full_dfs_path_from_dentry(dentry);
+       if (full_path == NULL) {
+               rc = -ENOMEM;
+               goto out_err;
+       }
+
+       rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,
+               &num_referrals, &referrals,
+               cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+       for (i = 0; i < num_referrals; i++) {
+               dump_referral(referrals+i);
+               /* connect to a storage node */
+               if (referrals[i].flags & DFSREF_STORAGE_SERVER) {
+                       int len;
+                       len = strlen(referrals[i].node_name);
+                       if (len < 2) {
+                               cERROR(1, ("%s: Net Address path too short: %s",
+                                       __FUNCTION__, referrals[i].node_name));
+                               rc = -EINVAL;
+                               goto out_err;
+                       }
+                       mnt = cifs_dfs_do_refmount(nd->mnt, nd->dentry,
+                                               referrals[i].node_name);
+                       cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",
+                                        __FUNCTION__,
+                                       referrals[i].node_name, mnt));
+
+                       /* complete mount procedure if we accured submount */
+                       if (!IS_ERR(mnt))
+                               break;
+               }
+       }
+
+       /* we need it cause for() above could exit without valid submount */
+       rc = PTR_ERR(mnt);
+       if (IS_ERR(mnt))
+               goto out_err;
+
+       nd->mnt->mnt_flags |= MNT_SHRINKABLE;
+       rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);
+
+out:
+       FreeXid(xid);
+       free_dfs_info_array(referrals, num_referrals);
+       kfree(full_path);
+       cFYI(1, ("leaving %s" , __FUNCTION__));
+       return ERR_PTR(rc);
+out_err:
+       path_release(nd);
+       goto out;
+}
+
+struct inode_operations cifs_dfs_referral_inode_operations = {
+       .follow_link = cifs_dfs_follow_mountpoint,
+};
+
index 34af556..8ad2330 100644 (file)
@@ -43,6 +43,9 @@ struct cifs_sb_info {
        mode_t  mnt_dir_mode;
        int     mnt_cifs_flags;
        int     prepathlen;
-       char   *prepath;
+       char   *prepath; /* relative path under the share to mount to */
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       char   *mountdata; /* mount options received at mount time */
+#endif
 };
 #endif                         /* _CIFS_FS_SB_H */
index 1529d2b..d543acc 100644 (file)
@@ -122,11 +122,13 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
        cFYI(1, ("key description = %s", description));
        spnego_key = request_key(&cifs_spnego_key_type, description, "");
 
+#ifdef CONFIG_CIFS_DEBUG2
        if (cifsFYI && !IS_ERR(spnego_key)) {
                struct cifs_spnego_msg *msg = spnego_key->payload.data;
-               cifs_dump_mem("SPNEGO reply blob:", msg->data,
-                               msg->secblob_len + msg->sesskey_len);
+               cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024,
+                               msg->secblob_len + msg->sesskey_len));
        }
+#endif /* CONFIG_CIFS_DEBUG2 */
 
 out:
        kfree(description);
index c312adc..a7035bd 100644 (file)
@@ -129,6 +129,54 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
        return (1); /* sids compare/match */
 }
 
+
+/* copy ntsd, owner sid, and group sid from a security descriptor to another */
+static void copy_sec_desc(const struct cifs_ntsd *pntsd,
+                               struct cifs_ntsd *pnntsd, __u32 sidsoffset)
+{
+       int i;
+
+       struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+       struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+
+       /* copy security descriptor control portion */
+       pnntsd->revision = pntsd->revision;
+       pnntsd->type = pntsd->type;
+       pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
+       pnntsd->sacloffset = 0;
+       pnntsd->osidoffset = cpu_to_le32(sidsoffset);
+       pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
+
+       /* copy owner sid */
+       owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->osidoffset));
+       nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
+
+       nowner_sid_ptr->revision = owner_sid_ptr->revision;
+       nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
+       for (i = 0; i < 6; i++)
+               nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
+       for (i = 0; i < 5; i++)
+               nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
+
+       /* copy group sid */
+       group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->gsidoffset));
+       ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
+                                       sizeof(struct cifs_sid));
+
+       ngroup_sid_ptr->revision = group_sid_ptr->revision;
+       ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
+       for (i = 0; i < 6; i++)
+               ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
+       for (i = 0; i < 5; i++)
+               ngroup_sid_ptr->sub_auth[i] =
+                               cpu_to_le32(group_sid_ptr->sub_auth[i]);
+
+       return;
+}
+
+
 /*
    change posix mode to reflect permissions
    pmode is the existing mode (we only want to overwrite part of this
@@ -220,6 +268,33 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
        return;
 }
 
+static __le16 fill_ace_for_sid(struct cifs_ace *pntace,
+                       const struct cifs_sid *psid, __u64 nmode, umode_t bits)
+{
+       int i;
+       __u16 size = 0;
+       __u32 access_req = 0;
+
+       pntace->type = ACCESS_ALLOWED;
+       pntace->flags = 0x0;
+       mode_to_access_flags(nmode, bits, &access_req);
+       if (!access_req)
+               access_req = SET_MINIMUM_RIGHTS;
+       pntace->access_req = cpu_to_le32(access_req);
+
+       pntace->sid.revision = psid->revision;
+       pntace->sid.num_subauth = psid->num_subauth;
+       for (i = 0; i < 6; i++)
+               pntace->sid.authority[i] = psid->authority[i];
+       for (i = 0; i < psid->num_subauth; i++)
+               pntace->sid.sub_auth[i] = psid->sub_auth[i];
+
+       size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
+       pntace->size = cpu_to_le16(size);
+
+       return (size);
+}
+
 
 #ifdef CONFIG_CIFS_DEBUG2
 static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
@@ -243,7 +318,7 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
                int i;
                cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d",
                        pace->sid.revision, pace->sid.num_subauth, pace->type,
-                       pace->flags, pace->size));
+                       pace->flags, le16_to_cpu(pace->size)));
                for (i = 0; i < num_subauth; ++i) {
                        cFYI(1, ("ACE sub_auth[%d]: 0x%x", i,
                                le32_to_cpu(pace->sid.sub_auth[i])));
@@ -346,6 +421,28 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
 }
 
 
+static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
+                       struct cifs_sid *pgrpsid, __u64 nmode)
+{
+       __le16 size = 0;
+       struct cifs_acl *pnndacl;
+
+       pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
+
+       size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
+                                       pownersid, nmode, S_IRWXU);
+       size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+                                       pgrpsid, nmode, S_IRWXG);
+       size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+                                        &sid_everyone, nmode, S_IRWXO);
+
+       pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
+       pndacl->num_aces = 3;
+
+       return (0);
+}
+
+
 static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
 {
        /* BB need to add parm so we can store the SID BB */
@@ -432,6 +529,46 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
 }
 
 
+/* Convert permission bits from mode to equivalent CIFS ACL */
+static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+                               int acl_len, struct inode *inode, __u64 nmode)
+{
+       int rc = 0;
+       __u32 dacloffset;
+       __u32 ndacloffset;
+       __u32 sidsoffset;
+       struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+       struct cifs_acl *dacl_ptr = NULL;  /* no need for SACL ptr */
+       struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
+
+       if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
+               return (-EIO);
+
+       owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->osidoffset));
+       group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->gsidoffset));
+
+       dacloffset = le32_to_cpu(pntsd->dacloffset);
+       dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+
+       ndacloffset = sizeof(struct cifs_ntsd);
+       ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
+       ndacl_ptr->revision = dacl_ptr->revision;
+       ndacl_ptr->size = 0;
+       ndacl_ptr->num_aces = 0;
+
+       rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
+
+       sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
+
+       /* copy security descriptor control portion and owner and group sid */
+       copy_sec_desc(pntsd, pnntsd, sidsoffset);
+
+       return (rc);
+}
+
+
 /* Retrieve an ACL from the server */
 static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
                                       const char *path)
@@ -487,6 +624,64 @@ static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
        return pntsd;
 }
 
+/* Set an ACL on the server */
+static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
+                               struct inode *inode, const char *path)
+{
+       struct cifsFileInfo *open_file;
+       int unlock_file = FALSE;
+       int xid;
+       int rc = -EIO;
+       __u16 fid;
+       struct super_block *sb;
+       struct cifs_sb_info *cifs_sb;
+
+#ifdef CONFIG_CIFS_DEBUG2
+       cFYI(1, ("set ACL for %s from mode 0x%x", path, inode->i_mode));
+#endif
+
+       if (!inode)
+               return (rc);
+
+       sb = inode->i_sb;
+       if (sb == NULL)
+               return (rc);
+
+       cifs_sb = CIFS_SB(sb);
+       xid = GetXid();
+
+       open_file = find_readable_file(CIFS_I(inode));
+       if (open_file) {
+               unlock_file = TRUE;
+               fid = open_file->netfid;
+       } else {
+               int oplock = FALSE;
+               /* open file */
+               rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
+                               WRITE_DAC, 0, &fid, &oplock, NULL,
+                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+               if (rc != 0) {
+                       cERROR(1, ("Unable to open file to set ACL"));
+                       FreeXid(xid);
+                       return (rc);
+               }
+       }
+
+       rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
+#ifdef CONFIG_CIFS_DEBUG2
+       cFYI(1, ("SetCIFSACL rc = %d", rc));
+#endif
+       if (unlock_file == TRUE)
+               atomic_dec(&open_file->wrtPending);
+       else
+               CIFSSMBClose(xid, cifs_sb->tcon, fid);
+
+       FreeXid(xid);
+
+       return (rc);
+}
+
 /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
 void acl_to_uid_mode(struct inode *inode, const char *path)
 {
@@ -510,24 +705,53 @@ void acl_to_uid_mode(struct inode *inode, const char *path)
 }
 
 /* Convert mode bits to an ACL so we can update the ACL on the server */
-int mode_to_acl(struct inode *inode, const char *path)
+int mode_to_acl(struct inode *inode, const char *path, __u64 nmode)
 {
        int rc = 0;
        __u32 acllen = 0;
-       struct cifs_ntsd *pntsd = NULL;
+       struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
+       struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
 
+#ifdef CONFIG_CIFS_DEBUG2
        cFYI(1, ("set ACL from mode for %s", path));
+#endif
 
        /* Get the security descriptor */
        pntsd = get_cifs_acl(&acllen, inode, path);
 
-       /* Add/Modify the three ACEs for owner, group, everyone
-          while retaining the other ACEs */
+       /* Add three ACEs for owner, group, everyone getting rid of
+          other ACEs as chmod disables ACEs and set the security descriptor */
 
-       /* Set the security descriptor */
+       if (pntsd) {
+               /* allocate memory for the smb header,
+                  set security descriptor request security descriptor
+                  parameters, and secuirty descriptor itself */
 
+               pnntsd = kmalloc(acllen, GFP_KERNEL);
+               if (!pnntsd) {
+                       cERROR(1, ("Unable to allocate security descriptor"));
+                       kfree(pntsd);
+                       return (-ENOMEM);
+               }
 
-       kfree(pntsd);
-       return rc;
+               rc = build_sec_desc(pntsd, pnntsd, acllen, inode, nmode);
+
+#ifdef CONFIG_CIFS_DEBUG2
+               cFYI(1, ("build_sec_desc rc: %d", rc));
+#endif
+
+               if (!rc) {
+                       /* Set the security descriptor */
+                       rc = set_cifs_acl(pnntsd, acllen, inode, path);
+#ifdef CONFIG_CIFS_DEBUG2
+                       cFYI(1, ("set_cifs_acl rc: %d", rc));
+#endif
+               }
+
+               kfree(pnntsd);
+               kfree(pntsd);
+       }
+
+       return (rc);
 }
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
index 093beaa..e9f4ec7 100644 (file)
@@ -44,6 +44,7 @@
 #include "cifs_fs_sb.h"
 #include <linux/mm.h>
 #include <linux/key-type.h>
+#include "dns_resolve.h"
 #include "cifs_spnego.h"
 #define CIFS_MAGIC_NUMBER 0xFF534D42   /* the first four bytes of SMB PDUs */
 
@@ -96,6 +97,9 @@ cifs_read_super(struct super_block *sb, void *data,
 {
        struct inode *inode;
        struct cifs_sb_info *cifs_sb;
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       int len;
+#endif
        int rc = 0;
 
        /* BB should we make this contingent on mount parm? */
@@ -105,6 +109,25 @@ cifs_read_super(struct super_block *sb, void *data,
        if (cifs_sb == NULL)
                return -ENOMEM;
 
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       /* copy mount params to sb for use in submounts */
+       /* BB: should we move this after the mount so we
+        * do not have to do the copy on failed mounts?
+        * BB: May be it is better to do simple copy before
+        * complex operation (mount), and in case of fail
+        * just exit instead of doing mount and attempting
+        * undo it if this copy fails?*/
+       len = strlen(data);
+       cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL);
+       if (cifs_sb->mountdata == NULL) {
+               kfree(sb->s_fs_info);
+               sb->s_fs_info = NULL;
+               return -ENOMEM;
+       }
+       strncpy(cifs_sb->mountdata, data, len + 1);
+       cifs_sb->mountdata[len] = '\0';
+#endif
+
        rc = cifs_mount(sb, cifs_sb, data, devname);
 
        if (rc) {
@@ -154,6 +177,12 @@ out_no_root:
 
 out_mount_failed:
        if (cifs_sb) {
+#ifdef CONFIG_CIFS_DFS_UPCALL
+               if (cifs_sb->mountdata) {
+                       kfree(cifs_sb->mountdata);
+                       cifs_sb->mountdata = NULL;
+               }
+#endif
                if (cifs_sb->local_nls)
                        unload_nls(cifs_sb->local_nls);
                kfree(cifs_sb);
@@ -177,6 +206,13 @@ cifs_put_super(struct super_block *sb)
        if (rc) {
                cERROR(1, ("cifs_umount failed with return code %d", rc));
        }
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       if (cifs_sb->mountdata) {
+               kfree(cifs_sb->mountdata);
+               cifs_sb->mountdata = NULL;
+       }
+#endif
+
        unload_nls(cifs_sb->local_nls);
        kfree(cifs_sb);
        return;
@@ -435,6 +471,10 @@ static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags)
        struct cifs_sb_info *cifs_sb;
        struct cifsTconInfo *tcon;
 
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       dfs_shrink_umount_helper(vfsmnt);
+#endif /* CONFIG CIFS_DFS_UPCALL */
+
        if (!(flags & MNT_FORCE))
                return;
        cifs_sb = CIFS_SB(vfsmnt->mnt_sb);
@@ -552,7 +592,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
        return remote_llseek(file, offset, origin);
 }
 
-static struct file_system_type cifs_fs_type = {
+struct file_system_type cifs_fs_type = {
        .owner = THIS_MODULE,
        .name = "cifs",
        .get_sb = cifs_get_sb,
@@ -1014,12 +1054,17 @@ init_cifs(void)
        rc = register_key_type(&cifs_spnego_key_type);
        if (rc)
                goto out_unregister_filesystem;
+#endif
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       rc = register_key_type(&key_type_dns_resolver);
+       if (rc)
+               goto out_unregister_key_type;
 #endif
        oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd");
        if (IS_ERR(oplockThread)) {
                rc = PTR_ERR(oplockThread);
                cERROR(1, ("error %d create oplock thread", rc));
-               goto out_unregister_key_type;
+               goto out_unregister_dfs_key_type;
        }
 
        dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd");
@@ -1033,7 +1078,11 @@ init_cifs(void)
 
  out_stop_oplock_thread:
        kthread_stop(oplockThread);
+ out_unregister_dfs_key_type:
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       unregister_key_type(&key_type_dns_resolver);
  out_unregister_key_type:
+#endif
 #ifdef CONFIG_CIFS_UPCALL
        unregister_key_type(&cifs_spnego_key_type);
  out_unregister_filesystem:
@@ -1059,6 +1108,9 @@ exit_cifs(void)
 #ifdef CONFIG_PROC_FS
        cifs_proc_clean();
 #endif
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       unregister_key_type(&key_type_dns_resolver);
+#endif
 #ifdef CONFIG_CIFS_UPCALL
        unregister_key_type(&cifs_spnego_key_type);
 #endif
index 2a21dc6..195b14d 100644 (file)
@@ -32,6 +32,7 @@
 #define TRUE 1
 #endif
 
+extern struct file_system_type cifs_fs_type;
 extern const struct address_space_operations cifs_addr_ops;
 extern const struct address_space_operations cifs_addr_ops_smallbuf;
 
@@ -60,6 +61,10 @@ extern int cifs_setattr(struct dentry *, struct iattr *);
 
 extern const struct inode_operations cifs_file_inode_ops;
 extern const struct inode_operations cifs_symlink_inode_ops;
+extern struct list_head cifs_dfs_automount_list;
+extern struct inode_operations cifs_dfs_referral_inode_operations;
+
+
 
 /* Functions related to files and directories */
 extern const struct file_operations cifs_file_ops;
index 1fde219..5d32d8d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsglob.h
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2007
+ *   Copyright (C) International Business Machines  Corp., 2002,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *              Jeremy Allison (jra@samba.org)
  *
 #define XATTR_DOS_ATTRIB "user.DOSATTRIB"
 #endif
 
-/*
- * This information is kept on every Server we know about.
- *
- * Some things to note:
- *
- */
-#define SERVER_NAME_LEN_WITH_NULL      (SERVER_NAME_LENGTH + 1)
-
 /*
  * CIFS vfs client Status information (based on what we know.)
  */
@@ -460,6 +452,37 @@ struct dir_notify_req {
        struct file *pfile;
 };
 
+struct dfs_info3_param {
+       int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/
+       int PathConsumed;
+       int server_type;
+       int ref_flag;
+       char *path_name;
+       char *node_name;
+};
+
+static inline void free_dfs_info_param(struct dfs_info3_param *param)
+{
+       if (param) {
+               kfree(param->path_name);
+               kfree(param->node_name);
+               kfree(param);
+       }
+}
+
+static inline void free_dfs_info_array(struct dfs_info3_param *param,
+                                      int number_of_items)
+{
+       int i;
+       if ((number_of_items == 0) || (param == NULL))
+               return;
+       for (i = 0; i < number_of_items; i++) {
+               kfree(param[i].path_name);
+               kfree(param[i].node_name);
+       }
+       kfree(param);
+}
+
 #define   MID_FREE 0
 #define   MID_REQUEST_ALLOCATED 1
 #define   MID_REQUEST_SUBMITTED 2
index dbe6b84..47f7950 100644 (file)
                                | DELETE | READ_CONTROL | WRITE_DAC \
                                | WRITE_OWNER | SYNCHRONIZE)
 
+#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
+                               | READ_CONTROL | SYNCHRONIZE)
+
 
 /*
  * Invalid readdir handle
index 8350eec..2f09f56 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsproto.h
  *
- *   Copyright (c) International Business Machines  Corp., 2002,2007
+ *   Copyright (c) International Business Machines  Corp., 2002,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -97,11 +97,14 @@ extern int cifs_get_inode_info_unix(struct inode **pinode,
                        const unsigned char *search_path,
                        struct super_block *sb, int xid);
 extern void acl_to_uid_mode(struct inode *inode, const char *search_path);
-extern int mode_to_acl(struct inode *inode, const char *path);
+extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
                        const char *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
+#ifdef CONFIG_CIFS_DFS_UPCALL
+extern void dfs_shrink_umount_helper(struct vfsmount *vfsmnt);
+#endif
 void cifs_proc_init(void);
 void cifs_proc_clean(void);
 
@@ -153,7 +156,7 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
                        const char *old_path,
                        const struct nls_table *nls_codepage,
                        unsigned int *pnum_referrals,
-                       unsigned char **preferrals,
+                       struct dfs_info3_param **preferrals,
                        int remap);
 extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
                                 struct super_block *sb, struct smb_vol *vol);
@@ -342,6 +345,8 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon,
                const struct nls_table *nls_codepage, int remap_special_chars);
 extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
                        __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
+extern int CIFSSMBSetCIFSACL(const int, struct cifsTconInfo *, __u16,
+                       struct cifs_ntsd *, __u32);
 extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
                const unsigned char *searchName,
                char *acl_inf, const int buflen, const int acl_type,
index 9e8a6be..9409524 100644 (file)
@@ -3156,6 +3156,71 @@ qsec_out:
 /*     cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
        return rc;
 }
+
+int
+CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
+                       struct cifs_ntsd *pntsd, __u32 acllen)
+{
+       __u16 byte_count, param_count, data_count, param_offset, data_offset;
+       int rc = 0;
+       int bytes_returned = 0;
+       SET_SEC_DESC_REQ *pSMB = NULL;
+       NTRANSACT_RSP *pSMBr = NULL;
+
+setCifsAclRetry:
+       rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
+                       (void **) &pSMBr);
+       if (rc)
+                       return (rc);
+
+       pSMB->MaxSetupCount = 0;
+       pSMB->Reserved = 0;
+
+       param_count = 8;
+       param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
+       data_count = acllen;
+       data_offset = param_offset + param_count;
+       byte_count = 3 /* pad */  + param_count;
+
+       pSMB->DataCount = cpu_to_le32(data_count);
+       pSMB->TotalDataCount = pSMB->DataCount;
+       pSMB->MaxParameterCount = cpu_to_le32(4);
+       pSMB->MaxDataCount = cpu_to_le32(16384);
+       pSMB->ParameterCount = cpu_to_le32(param_count);
+       pSMB->ParameterOffset = cpu_to_le32(param_offset);
+       pSMB->TotalParameterCount = pSMB->ParameterCount;
+       pSMB->DataOffset = cpu_to_le32(data_offset);
+       pSMB->SetupCount = 0;
+       pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
+       pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
+
+       pSMB->Fid = fid; /* file handle always le */
+       pSMB->Reserved2 = 0;
+       pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
+
+       if (pntsd && acllen) {
+               memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
+                       (char *) pntsd,
+                       acllen);
+               pSMB->hdr.smb_buf_length += (byte_count + data_count);
+
+       } else
+               pSMB->hdr.smb_buf_length += byte_count;
+
+       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+               (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+       cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
+       if (rc)
+               cFYI(1, ("Set CIFS ACL returned %d", rc));
+       cifs_buf_release(pSMB);
+
+       if (rc == -EAGAIN)
+               goto setCifsAclRetry;
+
+       return (rc);
+}
+
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
 
 /* Legacy Query Path Information call for lookup to old servers such
@@ -5499,7 +5564,7 @@ SetEARetry:
        else
                name_len = strnlen(ea_name, 255);
 
-       count = sizeof(*parm_data) + ea_value_len + name_len + 1;
+       count = sizeof(*parm_data) + ea_value_len + name_len;
        pSMB->MaxParameterCount = cpu_to_le16(2);
        pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
        pSMB->MaxSetupCount = 0;
index fd9147c..65d0ba7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/connect.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2007
+ *   Copyright (C) International Business Machines  Corp., 2002,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -1410,7 +1410,7 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
                    const char *old_path, const struct nls_table *nls_codepage,
                    int remap)
 {
-       unsigned char *referrals = NULL;
+       struct dfs_info3_param *referrals = NULL;
        unsigned int num_referrals;
        int rc = 0;
 
@@ -1429,12 +1429,14 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
 int
 get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
             const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
-            unsigned char **preferrals, int remap)
+            struct dfs_info3_param **preferrals, int remap)
 {
        char *temp_unc;
        int rc = 0;
+       unsigned char *targetUNCs;
 
        *pnum_referrals = 0;
+       *preferrals = NULL;
 
        if (pSesInfo->ipc_tid == 0) {
                temp_unc = kmalloc(2 /* for slashes */ +
@@ -1454,8 +1456,10 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
                kfree(temp_unc);
        }
        if (rc == 0)
-               rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
+               rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, &targetUNCs,
                                     pnum_referrals, nls_codepage, remap);
+       /* BB map targetUNCs to dfs_info3 structures, here or
+               in CIFSGetDFSRefer BB */
 
        return rc;
 }
@@ -1964,7 +1968,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
        if (existingCifsSes) {
                pSesInfo = existingCifsSes;
-               cFYI(1, ("Existing smb sess found"));
+               cFYI(1, ("Existing smb sess found (status=%d)",
+                       pSesInfo->status));
+               down(&pSesInfo->sesSem);
+               if (pSesInfo->status == CifsNeedReconnect) {
+                       cFYI(1, ("Session needs reconnect"));
+                       rc = cifs_setup_session(xid, pSesInfo,
+                                               cifs_sb->local_nls);
+               }
+               up(&pSesInfo->sesSem);
        } else if (!rc) {
                cFYI(1, ("Existing smb sess not found"));
                pSesInfo = sesInfoAlloc();
@@ -3514,7 +3526,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
                sesInfoFree(ses);
 
        FreeXid(xid);
-       return rc;      /* BB check if we should always return zero here */
+       return rc;
 }
 
 int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
index 37dc97a..699ec11 100644 (file)
@@ -517,12 +517,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
                d_add(direntry, NULL);
        /*      if it was once a directory (but how can we tell?) we could do
                shrink_dcache_parent(direntry); */
-       } else {
-               cERROR(1, ("Error 0x%x on cifs_get_inode_info in lookup of %s",
-                          rc, full_path));
-               /* BB special case check for Access Denied - watch security
-               exposure of returning dir info implicitly via different rc
-               if file exists or not but no access BB */
+       } else if (rc != -EACCES) {
+               cERROR(1, ("Unexpected lookup error %d", rc));
+               /* We special case check for Access Denied - since that
+               is a common return code */
        }
 
        kfree(full_path);
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
new file mode 100644 (file)
index 0000000..ef7f438
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  fs/cifs/dns_resolve.c
+ *
+ *   Copyright (c) 2007 Igor Mammedov
+ *   Author(s): Igor Mammedov (niallain@gmail.com)
+ *              Steve French (sfrench@us.ibm.com)
+ *
+ *   Contains the CIFS DFS upcall routines used for hostname to
+ *   IP address translation.
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <keys/user-type.h>
+#include "dns_resolve.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+static int dns_resolver_instantiate(struct key *key, const void *data,
+               size_t datalen)
+{
+       int rc = 0;
+       char *ip;
+
+       ip = kmalloc(datalen+1, GFP_KERNEL);
+       if (!ip)
+               return -ENOMEM;
+
+       memcpy(ip, data, datalen);
+       ip[datalen] = '\0';
+
+       rcu_assign_pointer(key->payload.data, ip);
+
+       return rc;
+}
+
+struct key_type key_type_dns_resolver = {
+       .name        = "dns_resolver",
+       .def_datalen = sizeof(struct in_addr),
+       .describe    = user_describe,
+       .instantiate = dns_resolver_instantiate,
+       .match       = user_match,
+};
+
+
+/* Resolves server name to ip address.
+ * input:
+ *     unc - server UNC
+ * output:
+ *     *ip_addr - pointer to server ip, caller responcible for freeing it.
+ * return 0 on success
+ */
+int
+dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+{
+       int rc = -EAGAIN;
+       struct key *rkey;
+       char *name;
+       int len;
+
+       if (!ip_addr || !unc)
+               return -EINVAL;
+
+       /* search for server name delimiter */
+       len = strlen(unc);
+       if (len < 3) {
+               cFYI(1, ("%s: unc is too short: %s", __FUNCTION__, unc));
+               return -EINVAL;
+       }
+       len -= 2;
+       name = memchr(unc+2, '\\', len);
+       if (!name) {
+               cFYI(1, ("%s: probably server name is whole unc: %s",
+                                       __FUNCTION__, unc));
+       } else {
+               len = (name - unc) - 2/* leading // */;
+       }
+
+       name = kmalloc(len+1, GFP_KERNEL);
+       if (!name) {
+               rc = -ENOMEM;
+               return rc;
+       }
+       memcpy(name, unc+2, len);
+       name[len] = 0;
+
+       rkey = request_key(&key_type_dns_resolver, name, "");
+       if (!IS_ERR(rkey)) {
+               len = strlen(rkey->payload.data);
+               *ip_addr = kmalloc(len+1, GFP_KERNEL);
+               if (*ip_addr) {
+                       memcpy(*ip_addr, rkey->payload.data, len);
+                       (*ip_addr)[len] = '\0';
+                       cFYI(1, ("%s: resolved: %s to %s", __FUNCTION__,
+                                       rkey->description,
+                                       *ip_addr
+                               ));
+                       rc = 0;
+               } else {
+                       rc = -ENOMEM;
+               }
+               key_put(rkey);
+       } else {
+               cERROR(1, ("%s: unable to resolve: %s", __FUNCTION__, name));
+       }
+
+       kfree(name);
+       return rc;
+}
+
+
diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
new file mode 100644 (file)
index 0000000..073fdc3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *   fs/cifs/dns_resolve.h -- DNS Resolver upcall management for CIFS DFS
+ *                            Handles host name to IP address resolution
+ * 
+ *   Copyright (c) International Business Machines  Corp., 2008
+ *   Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DNS_RESOLVE_H
+#define _DNS_RESOLVE_H
+
+#ifdef __KERNEL__
+#include <linux/key-type.h>
+extern struct key_type key_type_dns_resolver;
+extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
+#endif /* KERNEL */
+
+#endif /* _DNS_RESOLVE_H */
index dd26e27..5f7c374 100644 (file)
@@ -1179,12 +1179,10 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
                atomic_dec(&open_file->wrtPending);
                /* Does mm or vfs already set times? */
                inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
-               if ((bytes_written > 0) && (offset)) {
+               if ((bytes_written > 0) && (offset))
                        rc = 0;
-               } else if (bytes_written < 0) {
-                       if (rc != -EBADF)
-                               rc = bytes_written;
-               }
+               else if (bytes_written < 0)
+                       rc = bytes_written;
        } else {
                cFYI(1, ("No writeable filehandles for inode"));
                rc = -EIO;
index e915eb1..d9567ba 100644 (file)
@@ -54,9 +54,9 @@ int cifs_get_inode_info_unix(struct inode **pinode,
                                            MAX_TREE_SIZE + 1) +
                                    strnlen(search_path, MAX_PATHCONF) + 1,
                                    GFP_KERNEL);
-                       if (tmp_path == NULL) {
+                       if (tmp_path == NULL)
                                return -ENOMEM;
-                       }
+
                        /* have to skip first of the double backslash of
                           UNC name */
                        strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
@@ -511,7 +511,8 @@ int cifs_get_inode_info(struct inode **pinode,
                }
 
                spin_lock(&inode->i_lock);
-               if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) {
+               if (is_size_safe_to_change(cifsInfo,
+                                          le64_to_cpu(pfindData->EndOfFile))) {
                        /* can not safely shrink the file size here if the
                           client is writing to it due to potential races */
                        i_size_write(inode, le64_to_cpu(pfindData->EndOfFile));
@@ -931,7 +932,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
                (CIFS_UNIX_POSIX_PATH_OPS_CAP &
                        le64_to_cpu(pTcon->fsUnixInfo.Capability))) {
                u32 oplock = 0;
-               FILE_UNIX_BASIC_INFO * pInfo =
+               FILE_UNIX_BASIC_INFO *pInfo =
                        kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
                if (pInfo == NULL) {
                        rc = -ENOMEM;
@@ -1607,7 +1608,14 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
        else if (attrs->ia_valid & ATTR_MODE) {
                rc = 0;
-               if ((mode & S_IWUGO) == 0) /* not writeable */ {
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+                       rc = mode_to_acl(direntry->d_inode, full_path, mode);
+               else if ((mode & S_IWUGO) == 0) {
+#else
+               if ((mode & S_IWUGO) == 0) {
+#endif
+                       /* not writeable */
                        if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
                                set_dosattr = TRUE;
                                time_buf.Attributes =
@@ -1626,10 +1634,10 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                        if (time_buf.Attributes == 0)
                                time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL);
                }
-               /* BB to be implemented -
-                  via Windows security descriptors or streams */
-               /* CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, uid, gid,
-                                     cifs_sb->local_nls); */
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+                       mode_to_acl(direntry->d_inode, full_path, mode);
+#endif
        }
 
        if (attrs->ia_valid & ATTR_ATIME) {
index 11f2657..1d6fb01 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/link.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2003
+ *   Copyright (C) International Business Machines  Corp., 2002,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -236,8 +236,6 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
        char *full_path = NULL;
        char *tmp_path = NULL;
        char *tmpbuffer;
-       unsigned char *referrals = NULL;
-       unsigned int num_referrals = 0;
        int len;
        __u16 fid;
 
@@ -297,8 +295,11 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                                cFYI(1, ("Error closing junction point "
                                         "(open for ioctl)"));
                        }
+                       /* BB unwind this long, nested function, or remove BB */
                        if (rc == -EIO) {
                                /* Query if DFS Junction */
+                               unsigned int num_referrals = 0;
+                               struct dfs_info3_param *refs = NULL;
                                tmp_path =
                                        kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1,
                                                GFP_KERNEL);
@@ -310,7 +311,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                                        rc = get_dfs_path(xid, pTcon->ses,
                                                tmp_path,
                                                cifs_sb->local_nls,
-                                               &num_referrals, &referrals,
+                                               &num_referrals, &refs,
                                                cifs_sb->mnt_cifs_flags &
                                                    CIFS_MOUNT_MAP_SPECIAL_CHR);
                                        cFYI(1, ("Get DFS for %s rc = %d ",
@@ -320,14 +321,13 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
                                        else {
                                                cFYI(1, ("num referral: %d",
                                                        num_referrals));
-                                               if (referrals) {
-                                                       cFYI(1,("referral string: %s", referrals));
+                                               if (refs && refs->path_name) {
                                                        strncpy(tmpbuffer,
-                                                               referrals,
+                                                               refs->path_name,
                                                                len-1);
                                                }
                                        }
-                                       kfree(referrals);
+                                       kfree(refs);
                                        kfree(tmp_path);
 }
                                /* BB add code like else decode referrals
index d0cb469..d2153ab 100644 (file)
@@ -528,9 +528,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                        rc = -EOVERFLOW;
                        goto ssetup_exit;
                }
-               ses->server->mac_signing_key.len = msg->sesskey_len;
-               memcpy(ses->server->mac_signing_key.data.krb5, msg->data,
-                       msg->sesskey_len);
+               if (first_time) {
+                       ses->server->mac_signing_key.len = msg->sesskey_len;
+                       memcpy(ses->server->mac_signing_key.data.krb5,
+                               msg->data, msg->sesskey_len);
+               }
                pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
                capabilities |= CAP_EXTENDED_SECURITY;
                pSMB->req.Capabilities = cpu_to_le32(capabilities);
@@ -540,7 +542,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 
                if (ses->capabilities & CAP_UNICODE) {
                        /* unicode strings must be word aligned */
-                       if (iov[0].iov_len % 2) {
+                       if ((iov[0].iov_len + iov[1].iov_len) % 2) {
                                *bcc_ptr = 0;
                                bcc_ptr++;
                        }