diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/CHANGES | 5 | ||||
-rw-r--r-- | fs/cifs/Makefile | 2 | ||||
-rw-r--r-- | fs/cifs/README | 28 | ||||
-rw-r--r-- | fs/cifs/TODO | 14 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 377 | ||||
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifs_spnego.c | 6 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 240 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 56 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 41 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 11 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 67 | ||||
-rw-r--r-- | fs/cifs/connect.c | 24 | ||||
-rw-r--r-- | fs/cifs/dir.c | 10 | ||||
-rw-r--r-- | fs/cifs/dns_resolve.c | 124 | ||||
-rw-r--r-- | fs/cifs/dns_resolve.h | 32 | ||||
-rw-r--r-- | fs/cifs/file.c | 8 | ||||
-rw-r--r-- | fs/cifs/inode.c | 26 | ||||
-rw-r--r-- | fs/cifs/link.c | 16 | ||||
-rw-r--r-- | fs/cifs/sess.c | 10 |
22 files changed, 1026 insertions, 84 deletions
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index a609599287a..edd248367b3 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -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 ------------ diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 45e42fb97c1..6ba43fb346f 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -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 diff --git a/fs/cifs/README b/fs/cifs/README index bf11329ac78..c623e2f9c5d 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -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. diff --git a/fs/cifs/TODO b/fs/cifs/TODO index a8852c20072..92c9feac440 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -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 index 00000000000..413ee2349d1 --- /dev/null +++ b/fs/cifs/cifs_dfs_ref.c @@ -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, +}; + diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 34af556cdd8..8ad2330ba06 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -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 */ diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 1529d2b12e9..d543accc10d 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -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); diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index c312adcba4f..a7035bd18e4 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -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 */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 093beaa3900..e9f4ec70109 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -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 = |