Commit f82e7b57 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 4f9b3a37 a7a519a4
......@@ -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;
......
......@@ -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 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
}
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");
......
......@@ -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);
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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,
......
......@@ -411,6 +411,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
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 {
......
......@@ -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 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
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
......
......@@ -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;
}
......
......@@ -27,6 +27,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
*/
......
......@@ -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 @@ cifs_get_inode_info(struct inode **inode,
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,
......
......@@ -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
......
......@@ -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
......
......@@ -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 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
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,
......
......@@ -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
......@@ -3456,6 +3530,19 @@ int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
NULL);
}
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,
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment