diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-13 23:43:56 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-13 23:43:56 +0300 |
commit | f82e7b57b5fc48199e2f26ffafe2f96f7338ad3d (patch) | |
tree | 600f2f00d844ce755d08ff154cc629eb4f842586 | |
parent | 4f9b3a377549e2b585f5e1910c247913b49e6c83 (diff) | |
parent | a7a519a4926214ba4161bc30109f4a8d69defb8d (diff) | |
download | linux-f82e7b57b5fc48199e2f26ffafe2f96f7338ad3d.tar.xz |
Merge tag '5.8-rc-smb3-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
Pull more cifs updates from Steve French:
"12 cifs/smb3 fixes, 2 for stable.
- add support for idsfromsid on create and chgrp/chown allowing
ability to save owner information more naturally for some workloads
- improve query info (getattr) when SMB3.1.1 posix extensions are
negotiated by using new query info level"
* tag '5.8-rc-smb3-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6:
smb3: Add debug message for new file creation with idsfromsid mount option
cifs: fix chown and chgrp when idsfromsid mount option enabled
smb3: allow uid and gid owners to be set on create with idsfromsid mount option
smb311: Add tracepoints for new compound posix query info
smb311: add support for using info level for posix extensions query
smb311: Add support for lookup with posix extensions query info
smb311: Add support for SMB311 query info (non-compounded)
SMB311: Add support for query info using posix extensions (level 100)
smb3: add indatalen that can be a non-zero value to calculation of credit charge in smb2 ioctl
smb3: fix typo in mount options displayed in /proc/mounts
cifs: Add get_security_type_str function to return sec type.
smb3: extend fscache mount volume coherency check
-rw-r--r-- | fs/cifs/cache.c | 9 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 4 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 79 | ||||
-rw-r--r-- | fs/cifs/cifsacl.h | 15 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 18 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 3 | ||||
-rw-r--r-- | fs/cifs/dir.c | 5 | ||||
-rw-r--r-- | fs/cifs/file.c | 5 | ||||
-rw-r--r-- | fs/cifs/fscache.c | 17 | ||||
-rw-r--r-- | fs/cifs/fscache.h | 9 | ||||
-rw-r--r-- | fs/cifs/inode.c | 185 | ||||
-rw-r--r-- | fs/cifs/link.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2glob.h | 1 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 105 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 131 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 27 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 6 | ||||
-rw-r--r-- | fs/cifs/trace.h | 3 |
19 files changed, 571 insertions, 57 deletions
diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index b7420e605b28..0f2adecb94f2 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -53,13 +53,6 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = { .type = FSCACHE_COOKIE_TYPE_INDEX, }; -/* - * Auxiliary data attached to CIFS superblock within the cache - */ -struct cifs_fscache_super_auxdata { - u64 resource_id; /* unique server resource id */ -}; - char *extract_sharename(const char *treename) { const char *src; @@ -98,6 +91,8 @@ fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data, memset(&auxdata, 0, sizeof(auxdata)); auxdata.resource_id = tcon->resource_id; + auxdata.vol_create_time = tcon->vol_create_time; + auxdata.vol_serial_number = tcon->vol_serial_number; if (memcmp(data, &auxdata, datalen) != 0) return FSCACHE_CHECKAUX_OBSOLETE; diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 3ad1a98fd567..fc98b97b396a 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -221,8 +221,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) struct cifs_ses *ses; struct cifs_tcon *tcon; int i, j; - const char *security_types[] = {"Unspecified", "LANMAN", "NTLM", - "NTLMv2", "RawNTLMSSP", "Kerberos"}; seq_puts(m, "Display Internal CIFS Data Structures for Debugging\n" @@ -379,7 +377,7 @@ skip_rdma: } seq_printf(m,"Security type: %s\n", - security_types[server->ops->select_sectype(server, ses->sectype)]); + get_security_type_str(server->ops->select_sectype(server, ses->sectype))); if (server->rdma) seq_printf(m, "RDMA\n\t"); diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index ae421634aa42..6025d7fc7bbf 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -849,6 +849,28 @@ unsigned int setup_special_mode_ACE(struct cifs_ace *pntace, __u64 nmode) return ace_size; } +unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace) +{ + int i; + unsigned int ace_size = 28; + + pntace->type = ACCESS_ALLOWED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = cpu_to_le32(GENERIC_ALL); + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_unix_NFS_users.authority[i]; + + pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val); + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, __u64 nmode, bool modefromsid) { @@ -978,7 +1000,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, /* Convert permission bits from mode to equivalent CIFS ACL */ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, - bool mode_from_sid, int *aclflag) + bool mode_from_sid, bool id_from_sid, int *aclflag) { int rc = 0; __u32 dacloffset; @@ -1019,12 +1041,23 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, if (!nowner_sid_ptr) return -ENOMEM; id = from_kuid(&init_user_ns, uid); - rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); - if (rc) { - cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", - __func__, rc, id); - kfree(nowner_sid_ptr); - return rc; + if (id_from_sid) { + struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; + /* Populate the user ownership fields S-1-5-88-1 */ + osid->Revision = 1; + osid->NumAuth = 3; + osid->Authority[5] = 5; + osid->SubAuthorities[0] = cpu_to_le32(88); + osid->SubAuthorities[1] = cpu_to_le32(1); + osid->SubAuthorities[2] = cpu_to_le32(id); + } else { /* lookup sid with upcall */ + rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); + if (rc) { + cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", + __func__, rc, id); + kfree(nowner_sid_ptr); + return rc; + } } cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr); kfree(nowner_sid_ptr); @@ -1039,12 +1072,23 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, if (!ngroup_sid_ptr) return -ENOMEM; id = from_kgid(&init_user_ns, gid); - rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); - if (rc) { - cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", - __func__, rc, id); - kfree(ngroup_sid_ptr); - return rc; + if (id_from_sid) { + struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; + /* Populate the group ownership fields S-1-5-88-2 */ + gsid->Revision = 1; + gsid->NumAuth = 3; + gsid->Authority[5] = 5; + gsid->SubAuthorities[0] = cpu_to_le32(88); + gsid->SubAuthorities[1] = cpu_to_le32(2); + gsid->SubAuthorities[2] = cpu_to_le32(id); + } else { /* lookup sid with upcall */ + rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); + if (rc) { + cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", + __func__, rc, id); + kfree(ngroup_sid_ptr); + return rc; + } } cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr); kfree(ngroup_sid_ptr); @@ -1247,7 +1291,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); struct smb_version_operations *ops; - bool mode_from_sid; + bool mode_from_sid, id_from_sid; if (IS_ERR(tlink)) return PTR_ERR(tlink); @@ -1290,8 +1334,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, else mode_from_sid = false; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + id_from_sid = true; + else + id_from_sid = false; + rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid, - mode_from_sid, &aclflag); + mode_from_sid, id_from_sid, &aclflag); cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 21d7dee98d01..17562ea00e18 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -176,6 +176,21 @@ struct smb3_acl { __le16 Sbz2; /* MBZ */ } __packed; +/* + * Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid + * See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ +struct owner_sid { + u8 Revision; + u8 NumAuth; + u8 Authority[6]; + __le32 SubAuthorities[3]; +} __packed; + +struct owner_group_sids { + struct owner_sid owner; + struct owner_sid group; +} __packed; /* * Minimum security identifier can be one for system defined Users diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 889f9c71049b..0fb99d25e8a8 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -623,7 +623,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); if (tcon->ses->chan_max > 1) - seq_printf(s, ",multichannel,max_channel=%zu", + seq_printf(s, ",multichannel,max_channels=%zu", tcon->ses->chan_max); return 0; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e133bb3e172f..fad37d61910a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -2008,6 +2008,24 @@ extern struct smb_version_values smb302_values; extern struct smb_version_operations smb311_operations; extern struct smb_version_values smb311_values; +static inline char *get_security_type_str(enum securityEnum sectype) +{ + switch (sectype) { + case RawNTLMSSP: + return "RawNTLMSSP"; + case Kerberos: + return "Kerberos"; + case NTLMv2: + return "NTLMv2"; + case NTLM: + return "NTLM"; + case LANMAN: + return "LANMAN"; + default: + return "Unknown"; + } +} + static inline bool is_smb1_server(struct TCP_Server_Info *server) { return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index bd92070ca30c..7a836ec0438e 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -198,6 +198,8 @@ extern struct inode *cifs_iget(struct super_block *sb, extern int cifs_get_inode_info(struct inode **inode, const char *full_path, FILE_ALL_INFO *data, struct super_block *sb, int xid, const struct cifs_fid *fid); +extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, + struct super_block *sb, unsigned int xid); extern int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, unsigned int xid); @@ -220,6 +222,7 @@ extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, const char *, int); extern unsigned int setup_authusers_ACE(struct cifs_ace *pace); extern unsigned int setup_special_mode_ACE(struct cifs_ace *pace, __u64 nmode); +extern unsigned int setup_special_user_owner_ACE(struct cifs_ace *pace); extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 36e7b2fd2190..398c1eef7190 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -411,6 +411,7 @@ cifs_create_get_file_info: rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); else { + /* TODO: Add support for calling POSIX query info here, but passing in fid */ rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid); if (newinode) { @@ -700,7 +701,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, d_inode(direntry)); - if (pTcon->unix_ext) { + if (pTcon->posix_extensions) + rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid); + else if (pTcon->unix_ext) { rc = cifs_get_inode_info_unix(&newInode, full_path, parent_dir_inode->i_sb, xid); } else { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 8277859d12a3..4fe757cfc360 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -243,6 +243,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, if (rc) goto out; + /* TODO: Add support for calling posix query info but with passing in fid */ if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); @@ -800,7 +801,9 @@ reopen_success: if (!is_interrupt_error(rc)) mapping_set_error(inode->i_mapping, rc); - if (tcon->unix_ext) + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid); + else if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); else diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index ea6ace9c2417..da688185403c 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -96,6 +96,7 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { struct TCP_Server_Info *server = tcon->ses->server; char *sharename; + struct cifs_fscache_super_auxdata auxdata; sharename = extract_sharename(tcon->treeName); if (IS_ERR(sharename)) { @@ -104,11 +105,16 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) return; } + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.resource_id = tcon->resource_id; + auxdata.vol_create_time = tcon->vol_create_time; + auxdata.vol_serial_number = tcon->vol_serial_number; + tcon->fscache = fscache_acquire_cookie(server->fscache, &cifs_fscache_super_index_def, sharename, strlen(sharename), - &tcon->resource_id, sizeof(tcon->resource_id), + &auxdata, sizeof(auxdata), tcon, 0, true); kfree(sharename); cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", @@ -117,8 +123,15 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) { + struct cifs_fscache_super_auxdata auxdata; + + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.resource_id = tcon->resource_id; + auxdata.vol_create_time = tcon->vol_create_time; + auxdata.vol_serial_number = tcon->vol_serial_number; + cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); - fscache_relinquish_cookie(tcon->fscache, &tcon->resource_id, false); + fscache_relinquish_cookie(tcon->fscache, &auxdata, false); tcon->fscache = NULL; } diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 8c0862e41306..1091633d2adb 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -28,6 +28,15 @@ #ifdef CONFIG_CIFS_FSCACHE /* + * Auxiliary data attached to CIFS superblock within the cache + */ +struct cifs_fscache_super_auxdata { + u64 resource_id; /* unique server resource id */ + __le64 vol_create_time; + u32 vol_serial_number; +} __packed; + +/* * Auxiliary data attached to CIFS inode within the cache */ struct cifs_fscache_inode_auxdata { diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 5072bcaf4be1..583f5e4008c2 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -32,6 +32,7 @@ #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" +#include "smb2proto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" #include "cifs_unicode.h" @@ -595,6 +596,62 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, #endif } +/* Fill a cifs_fattr struct with info from POSIX info struct */ +static void +smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *info, + struct super_block *sb, bool adjust_tz, bool symlink) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + memset(fattr, 0, sizeof(*fattr)); + + /* no fattr->flags to set */ + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); + fattr->cf_uniqueid = le64_to_cpu(info->Inode); + + if (info->LastAccessTime) + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + else + ktime_get_coarse_real_ts64(&fattr->cf_atime); + + fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + + if (adjust_tz) { + fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; + } + + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); + /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ + /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ + + if (symlink) { + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { /* file */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } + /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ + + fattr->cf_uid = cifs_sb->mnt_uid; /* TODO: map uid and gid from SID */ + fattr->cf_gid = cifs_sb->mnt_gid; + + cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", + fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); +} + + /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */ static void cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, @@ -1023,6 +1080,121 @@ out: return rc; } +int +smb311_posix_get_inode_info(struct inode **inode, + const char *full_path, + struct super_block *sb, unsigned int xid) +{ + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + bool adjust_tz = false; + struct cifs_fattr fattr = {0}; + bool symlink = false; + struct smb311_posix_qinfo *data = NULL; + int rc = 0; + int tmprc = 0; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + /* + * 1. Fetch file metadata + */ + + if (is_inode_cache_good(*inode)) { + cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); + goto out; + } + data = kmalloc(sizeof(struct smb311_posix_qinfo), GFP_KERNEL); + if (!data) { + rc = -ENOMEM; + goto out; + } + + rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, + full_path, data, + &adjust_tz, &symlink); + + /* + * 2. Convert it to internal cifs metadata (fattr) + */ + + switch (rc) { + case 0: + smb311_posix_info_to_fattr(&fattr, data, sb, adjust_tz, symlink); + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + break; + case -EACCES: + /* + * For SMB2 and later the backup intent flag + * is already sent if needed on open and there + * is no path based FindFirst operation to use + * to retry with so nothing we can do, bail out + */ + goto out; + default: + cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); + goto out; + } + + + /* + * 3. Tweak fattr based on mount options + */ + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + /* + * 4. Update inode with final fattr data + */ + + if (!*inode) { + *inode = cifs_iget(sb, &fattr); + if (!*inode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*inode)->time = 0; /* force reval */ + rc = -ESTALE; + goto out; + } + + /* if filetype is different, return error */ + if (unlikely(((*inode)->i_mode & S_IFMT) != + (fattr.cf_mode & S_IFMT))) { + CIFS_I(*inode)->time = 0; /* force reval */ + rc = -ESTALE; + goto out; + } + + cifs_fattr_to_inode(*inode, &fattr); + } +out: + cifs_put_tlink(tlink); + kfree(data); + return rc; +} + + static const struct inode_operations cifs_ipc_inode_ops = { .lookup = cifs_lookup, }; @@ -1161,7 +1333,10 @@ struct inode *cifs_root_iget(struct super_block *sb) } convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); - rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, path, sb, xid); + else + rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); iget_no_retry: if (!inode) { @@ -1517,7 +1692,9 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, int rc = 0; struct inode *inode = NULL; - if (tcon->unix_ext) + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); + else if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, xid); else @@ -2114,7 +2291,9 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry) dentry, cifs_get_time(dentry), jiffies); again: - if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) + if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); + else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, diff --git a/fs/cifs/link.c b/fs/cifs/link.c index c381d2d03ef6..94dab4309fbb 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -701,7 +701,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) cifs_sb_target->local_nls); */ if (rc == 0) { - if (pTcon->unix_ext) + if (pTcon->posix_extensions) + rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid); + else if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); else diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h index dd10f0ce4cd5..cf20f0b5d836 100644 --- a/fs/cifs/smb2glob.h +++ b/fs/cifs/smb2glob.h @@ -45,6 +45,7 @@ #define SMB2_OP_HARDLINK 8 #define SMB2_OP_SET_EOF 9 #define SMB2_OP_RMDIR 10 +#define SMB2_OP_POSIX_QUERY_INFO 11 /* Used when constructing chained read requests. */ #define CHAINED_REQUEST 1 diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 0a116fc490a9..b9db73687eaa 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -166,6 +166,40 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); break; + case SMB2_OP_POSIX_QUERY_INFO: + rqst[num_rqst].rq_iov = &vars->qi_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + if (cfile) + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + SMB_FIND_FILE_POSIX_INFO, + SMB2_O_INFO_FILE, 0, + /* TBD: fix following to allow for longer SIDs */ + sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); + else { + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + SMB_FIND_FILE_POSIX_INFO, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); + break; case SMB2_OP_DELETE: trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); break; @@ -379,6 +413,24 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, trace_smb3_query_info_compound_done(xid, ses->Suid, tcon->tid); break; + case SMB2_OP_POSIX_QUERY_INFO: + if (rc == 0) { + qi_rsp = (struct smb2_query_info_rsp *) + rsp_iov[1].iov_base; + rc = smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + le32_to_cpu(qi_rsp->OutputBufferLength), + &rsp_iov[1], sizeof(struct smb311_posix_qinfo) /* add SIDs */, ptr); + } + if (rqst[1].rq_iov) + SMB2_query_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + if (rc) + trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); + break; case SMB2_OP_DELETE: if (rc) trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); @@ -512,6 +564,59 @@ out: return rc; } + +int +smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct smb311_posix_qinfo *data, bool *adjust_tz, bool *symlink) +{ + int rc; + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct smb311_posix_qinfo *smb2_data; + + *adjust_tz = false; + *symlink = false; + + /* BB TODO: Make struct larger when add support for parsing owner SIDs */ + smb2_data = kzalloc(sizeof(struct smb311_posix_qinfo), + GFP_KERNEL); + if (smb2_data == NULL) + return -ENOMEM; + + /* + * BB TODO: Add support for using the cached root handle. + * Create SMB2_query_posix_info worker function to do non-compounded query + * when we already have an open file handle for this. For now this is fast enough + * (always using the compounded version). + */ + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, + ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile); + if (rc == -EOPNOTSUPP) { + /* BB TODO: When support for special files added to Samba re-verify this path */ + *symlink = true; + create_options |= OPEN_REPARSE_POINT; + + /* Failed on a symbolic link - query a reparse point info */ + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, + smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL); + } + if (rc) + goto out; + + /* TODO: will need to allow for the 2 SIDs when add support for getting owner UID/GID */ + memcpy(data, smb2_data, sizeof(struct smb311_posix_qinfo)); + +out: + kfree(smb2_data); + return rc; +} + int smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, struct cifs_tcon *tcon, const char *name, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ded96b529a4d..2f4cdd290c46 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2317,28 +2317,75 @@ add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) return 0; } +/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ +static void setup_owner_group_sids(char *buf) +{ + struct owner_group_sids *sids = (struct owner_group_sids *)buf; + + /* Populate the user ownership fields S-1-5-88-1 */ + sids->owner.Revision = 1; + sids->owner.NumAuth = 3; + sids->owner.Authority[5] = 5; + sids->owner.SubAuthorities[0] = cpu_to_le32(88); + sids->owner.SubAuthorities[1] = cpu_to_le32(1); + sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); + + /* Populate the group ownership fields S-1-5-88-2 */ + sids->group.Revision = 1; + sids->group.NumAuth = 3; + sids->group.Authority[5] = 5; + sids->group.SubAuthorities[0] = cpu_to_le32(88); + sids->group.SubAuthorities[1] = cpu_to_le32(2); + sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); + + cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); +} + /* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ static struct crt_sd_ctxt * -create_sd_buf(umode_t mode, unsigned int *len) +create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) { struct crt_sd_ctxt *buf; struct cifs_ace *pace; unsigned int sdlen, acelen; + unsigned int owner_offset = 0; + unsigned int group_offset = 0; + + *len = roundup(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 2), 8); + + if (set_owner) { + /* offset fields are from beginning of security descriptor not of create context */ + owner_offset = sizeof(struct smb3_acl) + (sizeof(struct cifs_ace) * 2); + + /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ + *len += sizeof(struct owner_group_sids); + } - *len = roundup(sizeof(struct crt_sd_ctxt) + sizeof(struct cifs_ace) * 2, - 8); buf = kzalloc(*len, GFP_KERNEL); if (buf == NULL) return buf; + if (set_owner) { + buf->sd.OffsetOwner = cpu_to_le32(owner_offset); + group_offset = owner_offset + sizeof(struct owner_sid); + buf->sd.OffsetGroup = cpu_to_le32(group_offset); + } else { + buf->sd.OffsetOwner = 0; + buf->sd.OffsetGroup = 0; + } + sdlen = sizeof(struct smb3_sd) + sizeof(struct smb3_acl) + 2 * sizeof(struct cifs_ace); + if (set_owner) { + sdlen += sizeof(struct owner_group_sids); + setup_owner_group_sids(owner_offset + sizeof(struct create_context) + 8 /* name */ + + (char *)buf); + } buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct crt_sd_ctxt, sd)); buf->ccontext.DataLength = cpu_to_le32(sdlen); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct crt_sd_ctxt, Name)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ buf->Name[0] = 'S'; @@ -2359,23 +2406,34 @@ create_sd_buf(umode_t mode, unsigned int *len) /* create one ACE to hold the mode embedded in reserved special SID */ pace = (struct cifs_ace *)(sizeof(struct crt_sd_ctxt) + (char *)buf); acelen = setup_special_mode_ACE(pace, (__u64)mode); + + if (set_owner) { + /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ + pace = (struct cifs_ace *)(acelen + (sizeof(struct crt_sd_ctxt) + (char *)buf)); + acelen += setup_special_user_owner_ACE(pace); + /* it does not appear necessary to add an ACE for the NFS group SID */ + buf->acl.AceCount = cpu_to_le16(3); + } else + buf->acl.AceCount = cpu_to_le16(2); + /* and one more ACE to allow access for authenticated users */ pace = (struct cifs_ace *)(acelen + (sizeof(struct crt_sd_ctxt) + (char *)buf)); acelen += setup_authusers_ACE(pace); + buf->acl.AclSize = cpu_to_le16(sizeof(struct cifs_acl) + acelen); - buf->acl.AceCount = cpu_to_le16(2); + return buf; } static int -add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) +add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) { struct smb2_create_req *req = iov[0].iov_base; unsigned int num = *num_iovec; unsigned int len = 0; - iov[num].iov_base = create_sd_buf(mode, &len); + iov[num].iov_base = create_sd_buf(mode, set_owner, &len); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = len; @@ -2764,21 +2822,35 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, return rc; } - if ((oparms->disposition != FILE_OPEN) && - (oparms->cifs_sb) && - (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && - (oparms->mode != ACL_NO_MODE)) { - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(iov[n_iov-1].iov_len); + if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { + bool set_mode; + bool set_owner; + + if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && + (oparms->mode != ACL_NO_MODE)) + set_mode = true; + else { + set_mode = false; + oparms->mode = ACL_NO_MODE; } - cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); - rc = add_sd_context(iov, &n_iov, oparms->mode); - if (rc) - return rc; + if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + set_owner = true; + else + set_owner = false; + + if (set_owner | set_mode) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); + } + + cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); + rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); + if (rc) + return rc; + } } if (n_iov > 2) { @@ -2973,7 +3045,9 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, * response size smaller. */ req->MaxOutputResponse = cpu_to_le32(max_response_size); - req->sync_hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(max_response_size, SMB2_MAX_BUFFER_SIZE)); + req->sync_hdr.CreditCharge = + cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), + SMB2_MAX_BUFFER_SIZE)); if (is_fsctl) req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); else @@ -3457,6 +3531,19 @@ int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, } int +SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) +{ + size_t output_len = sizeof(struct smb311_posix_qinfo *) + + (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); + *plen = 0; + + return query_info(xid, tcon, persistent_fid, volatile_fid, + SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, + output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); +} + +int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, void **data, u32 *plen) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 3b0e6acf9d7d..236814d61248 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -1653,7 +1653,7 @@ struct create_posix_rsp { } __packed; /* - * SMB2-only POSIX info level + * SMB2-only POSIX info level for query dir * * See posix_info_sid_size(), posix_info_extra_size() and * posix_info_parse() to help with the handling of this struct. @@ -1683,6 +1683,31 @@ struct smb2_posix_info { */ } __packed; +/* Level 100 query info */ +struct smb311_posix_qinfo { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 Sids[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + /* * Parsed version of the above struct. Allows direct access to the * variable length fields diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 71ba74792c9e..2f8ecbf54214 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -182,6 +182,8 @@ extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, struct TCP_Server_Info *server, u64 persistent_file_id, u64 volatile_file_id); extern void SMB2_flush_free(struct smb_rqst *rqst); +extern int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen); extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, struct smb2_file_all_info *data); @@ -289,6 +291,10 @@ extern int smb2_query_info_compound(const unsigned int xid, u32 class, u32 type, u32 output_len, struct kvec *rsp, int *buftype, struct cifs_sb_info *cifs_sb); +/* query path info from the server using SMB311 POSIX extensions*/ +extern int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *sb, const char *path, struct smb311_posix_qinfo *qinf, + bool *adjust_tx, bool *symlink); int posix_info_parse(const void *beg, const void *end, struct smb2_posix_info_parsed *out); int posix_info_sid_size(const void *beg, const void *end); diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index 4cb0d5f7ce45..eef4b08c7208 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -318,6 +318,7 @@ DEFINE_EVENT(smb3_inf_compound_enter_class, smb3_##name, \ TP_ARGS(xid, tid, sesid, full_path)) DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(posix_query_info_compound_enter); DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(hardlink_enter); DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter); DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); @@ -354,6 +355,7 @@ DEFINE_EVENT(smb3_inf_compound_done_class, smb3_##name, \ TP_ARGS(xid, tid, sesid)) DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(posix_query_info_compound_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(hardlink_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); @@ -395,6 +397,7 @@ DEFINE_EVENT(smb3_inf_compound_err_class, smb3_##name, \ TP_ARGS(xid, tid, sesid, rc)) DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(posix_query_info_compound_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(hardlink_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); |