cifs: improve symlink handling for smb2+

When creating inode for symlink, the client used to send below
requests to fill it in:

    * create+query_info+close (STATUS_STOPPED_ON_SYMLINK)
    * create(+reparse_flag)+query_info+close (set file attrs)
    * create+ioctl(get_reparse)+close (query reparse tag)

and then for every access to the symlink dentry, the ->link() method
would send another:

    * create+ioctl(get_reparse)+close (parse symlink)

So, in order to improve:

    (i) Get rid of unnecessary roundtrips and then resolve symlinks as
	follows:

        * create+query_info+close (STATUS_STOPPED_ON_SYMLINK +
	                           parse symlink + get reparse tag)
        * create(+reparse_flag)+query_info+close (set file attrs)

    (ii) Set the resolved symlink target directly in inode->i_link and
         use simple_get_link() for ->link() to simply return it.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Paulo Alcantara 2022-10-03 18:43:50 -03:00 committed by Steve French
parent 977bb65308
commit 76894f3e2f
14 changed files with 451 additions and 453 deletions

View File

@ -396,6 +396,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode->epoch = 0; cifs_inode->epoch = 0;
spin_lock_init(&cifs_inode->open_file_lock); spin_lock_init(&cifs_inode->open_file_lock);
generate_random_uuid(cifs_inode->lease_key); generate_random_uuid(cifs_inode->lease_key);
cifs_inode->symlink_target = NULL;
/* /*
* Can not set i_flags here - they get immediately overwritten to zero * Can not set i_flags here - they get immediately overwritten to zero
@ -412,7 +413,11 @@ cifs_alloc_inode(struct super_block *sb)
static void static void
cifs_free_inode(struct inode *inode) cifs_free_inode(struct inode *inode)
{ {
kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); struct cifsInodeInfo *cinode = CIFS_I(inode);
if (S_ISLNK(inode->i_mode))
kfree(cinode->symlink_target);
kmem_cache_free(cifs_inode_cachep, cinode);
} }
static void static void
@ -1139,7 +1144,7 @@ const struct inode_operations cifs_file_inode_ops = {
}; };
const struct inode_operations cifs_symlink_inode_ops = { const struct inode_operations cifs_symlink_inode_ops = {
.get_link = cifs_get_link, .get_link = simple_get_link,
.permission = cifs_permission, .permission = cifs_permission,
.listxattr = cifs_listxattr, .listxattr = cifs_listxattr,
}; };

View File

@ -185,6 +185,19 @@ struct cifs_cred {
struct cifs_ace *aces; struct cifs_ace *aces;
}; };
struct cifs_open_info_data {
char *symlink_target;
union {
struct smb2_file_all_info fi;
struct smb311_posix_qinfo posix_fi;
};
};
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
{
kfree(data->symlink_target);
}
/* /*
***************************************************************** *****************************************************************
* Except the CIFS PDUs themselves all the * Except the CIFS PDUs themselves all the
@ -307,20 +320,20 @@ struct smb_version_operations {
int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, int (*is_path_accessible)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const char *); struct cifs_sb_info *, const char *);
/* query path data from the server */ /* query path data from the server */
int (*query_path_info)(const unsigned int, struct cifs_tcon *, int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *, const char *, struct cifs_sb_info *cifs_sb, const char *full_path,
FILE_ALL_INFO *, bool *, bool *); struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
/* query file data from the server */ /* query file data from the server */
int (*query_file_info)(const unsigned int, struct cifs_tcon *, int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *, FILE_ALL_INFO *); struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
/* query reparse tag from srv to determine which type of special file */ /* query reparse tag from srv to determine which type of special file */
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *path, struct cifs_sb_info *cifs_sb, const char *path,
__u32 *reparse_tag); __u32 *reparse_tag);
/* get server index number */ /* get server index number */
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *, int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *, const char *, struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
u64 *uniqueid, FILE_ALL_INFO *); struct cifs_open_info_data *data);
/* set size by path */ /* set size by path */
int (*set_path_size)(const unsigned int, struct cifs_tcon *, int (*set_path_size)(const unsigned int, struct cifs_tcon *,
const char *, __u64, struct cifs_sb_info *, bool); const char *, __u64, struct cifs_sb_info *, bool);
@ -369,8 +382,8 @@ struct smb_version_operations {
struct cifs_sb_info *, const char *, struct cifs_sb_info *, const char *,
char **, bool); char **, bool);
/* open a file for non-posix mounts */ /* open a file for non-posix mounts */
int (*open)(const unsigned int, struct cifs_open_parms *, int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
__u32 *, FILE_ALL_INFO *); void *buf);
/* set fid protocol-specific info */ /* set fid protocol-specific info */
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
/* close a file */ /* close a file */
@ -1123,6 +1136,7 @@ struct cifs_fattr {
struct timespec64 cf_mtime; struct timespec64 cf_mtime;
struct timespec64 cf_ctime; struct timespec64 cf_ctime;
u32 cf_cifstag; u32 cf_cifstag;
char *cf_symlink_target;
}; };
/* /*
@ -1385,6 +1399,7 @@ struct cifsFileInfo {
struct work_struct put; /* work for the final part of _put */ struct work_struct put; /* work for the final part of _put */
struct delayed_work deferred; struct delayed_work deferred;
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */ bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
char *symlink_target;
}; };
struct cifs_io_parms { struct cifs_io_parms {
@ -1543,6 +1558,7 @@ struct cifsInodeInfo {
struct list_head deferred_closes; /* list of deferred closes */ struct list_head deferred_closes; /* list of deferred closes */
spinlock_t deferred_lock; /* protection on deferred list */ spinlock_t deferred_lock; /* protection on deferred list */
bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */ bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
char *symlink_target;
}; };
static inline struct cifsInodeInfo * static inline struct cifsInodeInfo *
@ -2111,4 +2127,14 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
return sizeof(ses->workstation_name); return sizeof(ses->workstation_name);
} }
static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src)
{
memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src));
dst->AccessFlags = src->AccessFlags;
dst->CurrentByteOffset = src->CurrentByteOffset;
dst->Mode = src->Mode;
dst->AlignmentRequirement = src->AlignmentRequirement;
dst->FileNameLength = src->FileNameLength;
}
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */

View File

@ -182,10 +182,9 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
extern void cifs_down_write(struct rw_semaphore *sem); extern void cifs_down_write(struct rw_semaphore *sem);
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct file *file, struct tcon_link *tlink, __u32 oplock,
struct tcon_link *tlink, const char *symlink_target);
__u32 oplock);
extern int cifs_posix_open(const char *full_path, struct inode **inode, extern int cifs_posix_open(const char *full_path, struct inode **inode,
struct super_block *sb, int mode, struct super_block *sb, int mode,
unsigned int f_flags, __u32 *oplock, __u16 *netfid, unsigned int f_flags, __u32 *oplock, __u16 *netfid,
@ -200,9 +199,9 @@ extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
extern struct inode *cifs_iget(struct super_block *sb, extern struct inode *cifs_iget(struct super_block *sb,
struct cifs_fattr *fattr); struct cifs_fattr *fattr);
extern int cifs_get_inode_info(struct inode **inode, const char *full_path, int cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb, struct cifs_open_info_data *data, struct super_block *sb, int xid,
int xid, const struct cifs_fid *fid); const struct cifs_fid *fid);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
struct super_block *sb, unsigned int xid); struct super_block *sb, unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode, extern int cifs_get_inode_info_unix(struct inode **pinode,

View File

@ -165,10 +165,9 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
/* Inode operations in similar order to how they appear in Linux file fs.h */ /* Inode operations in similar order to how they appear in Linux file fs.h */
static int static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
struct tcon_link *tlink, unsigned oflags, umode_t mode, struct cifs_fid *fid, struct cifs_open_info_data *buf)
__u32 *oplock, struct cifs_fid *fid)
{ {
int rc = -ENOENT; int rc = -ENOENT;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
@ -177,7 +176,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
struct cifs_tcon *tcon = tlink_tcon(tlink); struct cifs_tcon *tcon = tlink_tcon(tlink);
const char *full_path; const char *full_path;
void *page = alloc_dentry_path(); void *page = alloc_dentry_path();
FILE_ALL_INFO *buf = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
int disposition; int disposition;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
@ -290,12 +288,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
goto out; goto out;
} }
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
}
/* /*
* if we're not using unix extensions, see if we need to set * if we're not using unix extensions, see if we need to set
* ATTR_READONLY on the create call * ATTR_READONLY on the create call
@ -364,8 +356,7 @@ cifs_create_get_file_info:
{ {
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
/* TODO: Add support for calling POSIX query info here, but passing in fid */ /* 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, rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
xid, fid);
if (newinode) { if (newinode) {
if (server->ops->set_lease_key) if (server->ops->set_lease_key)
server->ops->set_lease_key(newinode, fid); server->ops->set_lease_key(newinode, fid);
@ -402,7 +393,6 @@ cifs_create_set_dentry:
d_add(direntry, newinode); d_add(direntry, newinode);
out: out:
kfree(buf);
free_dentry_path(page); free_dentry_path(page);
return rc; return rc;
@ -427,6 +417,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct cifs_pending_open open; struct cifs_pending_open open;
__u32 oplock; __u32 oplock;
struct cifsFileInfo *file_info; struct cifsFileInfo *file_info;
struct cifs_open_info_data buf = {};
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
return -EIO; return -EIO;
@ -484,8 +475,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
cifs_add_pending_open(&fid, tlink, &open); cifs_add_pending_open(&fid, tlink, &open);
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
&oplock, &fid); &oplock, &fid, &buf);
if (rc) { if (rc) {
cifs_del_pending_open(&open); cifs_del_pending_open(&open);
goto out; goto out;
@ -510,7 +500,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
file->f_op = &cifs_file_direct_ops; file->f_op = &cifs_file_direct_ops;
} }
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock); file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target);
if (file_info == NULL) { if (file_info == NULL) {
if (server->ops->close) if (server->ops->close)
server->ops->close(xid, tcon, &fid); server->ops->close(xid, tcon, &fid);
@ -526,6 +516,7 @@ out:
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
out_free_xid: out_free_xid:
free_xid(xid); free_xid(xid);
cifs_free_open_info(&buf);
return rc; return rc;
} }
@ -547,6 +538,7 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_fid fid; struct cifs_fid fid;
__u32 oplock; __u32 oplock;
struct cifs_open_info_data buf = {};
cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
inode, direntry, direntry); inode, direntry, direntry);
@ -565,11 +557,11 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
if (server->ops->new_lease_key) if (server->ops->new_lease_key)
server->ops->new_lease_key(&fid); server->ops->new_lease_key(&fid);
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf);
&oplock, &fid);
if (!rc && server->ops->close) if (!rc && server->ops->close)
server->ops->close(xid, tcon, &fid); server->ops->close(xid, tcon, &fid);
cifs_free_open_info(&buf);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
out_free_xid: out_free_xid:
free_xid(xid); free_xid(xid);

View File

@ -209,16 +209,14 @@ posix_open_ret:
} }
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
static int static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
struct cifs_fid *fid, unsigned int xid) struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf)
{ {
int rc; int rc;
int desired_access; int desired_access;
int disposition; int disposition;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
@ -255,10 +253,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
/* BB pass O_SYNC flag through on file attributes .. BB */ /* BB pass O_SYNC flag through on file attributes .. BB */
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* O_SYNC also has bit for O_DSYNC so following check picks up either */ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
if (f_flags & O_SYNC) if (f_flags & O_SYNC)
create_options |= CREATE_WRITE_THROUGH; create_options |= CREATE_WRITE_THROUGH;
@ -276,9 +270,8 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
oparms.reconnect = false; oparms.reconnect = false;
rc = server->ops->open(xid, &oparms, oplock, buf); rc = server->ops->open(xid, &oparms, oplock, buf);
if (rc) if (rc)
goto out; return rc;
/* TODO: Add support for calling posix query info but with passing in fid */ /* TODO: Add support for calling posix query info but with passing in fid */
if (tcon->unix_ext) if (tcon->unix_ext)
@ -294,8 +287,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
rc = -EOPENSTALE; rc = -EOPENSTALE;
} }
out:
kfree(buf);
return rc; return rc;
} }
@ -325,9 +316,9 @@ cifs_down_write(struct rw_semaphore *sem)
static void cifsFileInfo_put_work(struct work_struct *work); static void cifsFileInfo_put_work(struct work_struct *work);
struct cifsFileInfo * struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, struct tcon_link *tlink, __u32 oplock,
struct tcon_link *tlink, __u32 oplock) const char *symlink_target)
{ {
struct dentry *dentry = file_dentry(file); struct dentry *dentry = file_dentry(file);
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
@ -347,6 +338,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
return NULL; return NULL;
} }
if (symlink_target) {
cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL);
if (!cfile->symlink_target) {
kfree(fdlocks);
kfree(cfile);
return NULL;
}
}
INIT_LIST_HEAD(&fdlocks->locks); INIT_LIST_HEAD(&fdlocks->locks);
fdlocks->cfile = cfile; fdlocks->cfile = cfile;
cfile->llist = fdlocks; cfile->llist = fdlocks;
@ -440,6 +440,7 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
cifs_put_tlink(cifs_file->tlink); cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry); dput(cifs_file->dentry);
cifs_sb_deactive(sb); cifs_sb_deactive(sb);
kfree(cifs_file->symlink_target);
kfree(cifs_file); kfree(cifs_file);
} }
@ -572,6 +573,7 @@ int cifs_open(struct inode *inode, struct file *file)
bool posix_open_ok = false; bool posix_open_ok = false;
struct cifs_fid fid; struct cifs_fid fid;
struct cifs_pending_open open; struct cifs_pending_open open;
struct cifs_open_info_data data = {};
xid = get_xid(); xid = get_xid();
@ -662,15 +664,15 @@ int cifs_open(struct inode *inode, struct file *file)
if (server->ops->get_lease_key) if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid); server->ops->get_lease_key(inode, &fid);
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid,
file->f_flags, &oplock, &fid, xid); xid, &data);
if (rc) { if (rc) {
cifs_del_pending_open(&open); cifs_del_pending_open(&open);
goto out; goto out;
} }
} }
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock); cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target);
if (cfile == NULL) { if (cfile == NULL) {
if (server->ops->close) if (server->ops->close)
server->ops->close(xid, tcon, &fid); server->ops->close(xid, tcon, &fid);
@ -712,6 +714,7 @@ out:
free_dentry_path(page); free_dentry_path(page);
free_xid(xid); free_xid(xid);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
cifs_free_open_info(&data);
return rc; return rc;
} }

View File

@ -210,6 +210,17 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
*/ */
inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
} }
if (S_ISLNK(fattr->cf_mode)) {
kfree(cifs_i->symlink_target);
cifs_i->symlink_target = fattr->cf_symlink_target;
fattr->cf_symlink_target = NULL;
if (unlikely(!cifs_i->symlink_target))
inode->i_link = ERR_PTR(-EOPNOTSUPP);
else
inode->i_link = cifs_i->symlink_target;
}
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL)
@ -347,13 +358,20 @@ cifs_get_file_info_unix(struct file *filp)
int rc; int rc;
unsigned int xid; unsigned int xid;
FILE_UNIX_BASIC_INFO find_data; FILE_UNIX_BASIC_INFO find_data;
struct cifs_fattr fattr; struct cifs_fattr fattr = {};
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsFileInfo *cfile = filp->private_data; struct cifsFileInfo *cfile = filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
xid = get_xid(); xid = get_xid();
if (cfile->symlink_target) {
fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!fattr.cf_symlink_target)
return -ENOMEM;
}
rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data); rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);
if (!rc) { if (!rc) {
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
@ -378,6 +396,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
FILE_UNIX_BASIC_INFO find_data; FILE_UNIX_BASIC_INFO find_data;
struct cifs_fattr fattr; struct cifs_fattr fattr;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@ -387,10 +406,12 @@ int cifs_get_inode_info_unix(struct inode **pinode,
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/* could have done a find first instead but this returns more info */ /* could have done a find first instead but this returns more info */
rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
cifs_sb->local_nls, cifs_remap(cifs_sb)); cifs_sb->local_nls, cifs_remap(cifs_sb));
cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
if (!rc) { if (!rc) {
@ -410,6 +431,17 @@ int cifs_get_inode_info_unix(struct inode **pinode,
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
} }
if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) {
if (!server->ops->query_symlink)
return -EOPNOTSUPP;
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
&fattr.cf_symlink_target, false);
if (rc) {
cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
goto cgiiu_exit;
}
}
if (*pinode == NULL) { if (*pinode == NULL) {
/* get new inode */ /* get new inode */
cifs_fill_uniqueid(sb, &fattr); cifs_fill_uniqueid(sb, &fattr);
@ -432,6 +464,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
} }
cgiiu_exit: cgiiu_exit:
kfree(fattr.cf_symlink_target);
return rc; return rc;
} }
#else #else
@ -601,10 +634,10 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
} }
/* Fill a cifs_fattr struct with info from POSIX info struct */ /* Fill a cifs_fattr struct with info from POSIX info struct */
static void static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data,
smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *info,
struct super_block *sb, bool adjust_tz, bool symlink) struct super_block *sb, bool adjust_tz, bool symlink)
{ {
struct smb311_posix_qinfo *info = &data->posix_fi;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
@ -639,6 +672,8 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
if (symlink) { if (symlink) {
fattr->cf_mode |= S_IFLNK; fattr->cf_mode |= S_IFLNK;
fattr->cf_dtype = DT_LNK; fattr->cf_dtype = DT_LNK;
fattr->cf_symlink_target = data->symlink_target;
data->symlink_target = NULL;
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode |= S_IFDIR; fattr->cf_mode |= S_IFDIR;
fattr->cf_dtype = DT_DIR; fattr->cf_dtype = DT_DIR;
@ -655,13 +690,11 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
} }
static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data,
/* Fill a cifs_fattr struct with info from FILE_ALL_INFO */ struct super_block *sb, bool adjust_tz, bool symlink,
static void u32 reparse_tag)
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
struct super_block *sb, bool adjust_tz,
bool symlink, u32 reparse_tag)
{ {
struct smb2_file_all_info *info = &data->fi;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
@ -703,7 +736,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
} else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) { } else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_BLK; fattr->cf_dtype = DT_BLK;
} else if (symlink) { /* TODO add more reparse tag checks */ } else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK ||
reparse_tag == IO_REPARSE_TAG_NFS) {
fattr->cf_mode = S_IFLNK; fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK; fattr->cf_dtype = DT_LNK;
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
@ -735,6 +769,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
} }
} }
if (S_ISLNK(fattr->cf_mode)) {
fattr->cf_symlink_target = data->symlink_target;
data->symlink_target = NULL;
}
fattr->cf_uid = cifs_sb->ctx->linux_uid; fattr->cf_uid = cifs_sb->ctx->linux_uid;
fattr->cf_gid = cifs_sb->ctx->linux_gid; fattr->cf_gid = cifs_sb->ctx->linux_gid;
} }
@ -744,23 +783,28 @@ cifs_get_file_info(struct file *filp)
{ {
int rc; int rc;
unsigned int xid; unsigned int xid;
FILE_ALL_INFO find_data; struct cifs_open_info_data data = {};
struct cifs_fattr fattr; struct cifs_fattr fattr;
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct cifsFileInfo *cfile = filp->private_data; struct cifsFileInfo *cfile = filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
bool symlink = false;
u32 tag = 0;
if (!server->ops->query_file_info) if (!server->ops->query_file_info)
return -ENOSYS; return -ENOSYS;
xid = get_xid(); xid = get_xid();
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); rc = server->ops->query_file_info(xid, tcon, cfile, &data);
switch (rc) { switch (rc) {
case 0: case 0:
/* TODO: add support to query reparse tag */ /* TODO: add support to query reparse tag */
cifs_all_info_to_fattr(&fattr, &find_data, inode->i_sb, false, if (data.symlink_target) {
false, 0 /* no reparse tag */); symlink = true;
tag = IO_REPARSE_TAG_SYMLINK;
}
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag);
break; break;
case -EREMOTE: case -EREMOTE:
cifs_create_dfs_fattr(&fattr, inode->i_sb); cifs_create_dfs_fattr(&fattr, inode->i_sb);
@ -789,6 +833,7 @@ cifs_get_file_info(struct file *filp)
/* if filetype is different, return error */ /* if filetype is different, return error */
rc = cifs_fattr_to_inode(inode, &fattr); rc = cifs_fattr_to_inode(inode, &fattr);
cgfi_exit: cgfi_exit:
cifs_free_open_info(&data);
free_xid(xid); free_xid(xid);
return rc; return rc;
} }
@ -860,14 +905,9 @@ cifs_backup_query_path_info(int xid,
} }
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
static void static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb,
cifs_set_fattr_ino(int xid, struct inode **inode, const char *full_path,
struct cifs_tcon *tcon, struct cifs_open_info_data *data, struct cifs_fattr *fattr)
struct super_block *sb,
struct inode **inode,
const char *full_path,
FILE_ALL_INFO *data,
struct cifs_fattr *fattr)
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
@ -885,11 +925,8 @@ cifs_set_fattr_ino(int xid,
* If we have an inode pass a NULL tcon to ensure we don't * If we have an inode pass a NULL tcon to ensure we don't
* make a round trip to the server. This only works for SMB2+. * make a round trip to the server. This only works for SMB2+.
*/ */
rc = server->ops->get_srv_inum(xid, rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path,
*inode ? NULL : tcon, &fattr->cf_uniqueid, data);
cifs_sb, full_path,
&fattr->cf_uniqueid,
data);
if (rc) { if (rc) {
/* /*
* If that fails reuse existing ino or generate one * If that fails reuse existing ino or generate one
@ -923,14 +960,10 @@ static inline bool is_inode_cache_good(struct inode *ino)
return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
} }
int int cifs_get_inode_info(struct inode **inode, const char *full_path,
cifs_get_inode_info(struct inode **inode, struct cifs_open_info_data *data, struct super_block *sb, int xid,
const char *full_path,
FILE_ALL_INFO *in_data,
struct super_block *sb, int xid,
const struct cifs_fid *fid) const struct cifs_fid *fid)
{ {
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct tcon_link *tlink; struct tcon_link *tlink;
@ -938,8 +971,7 @@ cifs_get_inode_info(struct inode **inode,
bool adjust_tz = false; bool adjust_tz = false;
struct cifs_fattr fattr = {0}; struct cifs_fattr fattr = {0};
bool is_reparse_point = false; bool is_reparse_point = false;
FILE_ALL_INFO *data = in_data; struct cifs_open_info_data tmp_data = {};
FILE_ALL_INFO *tmp_data = NULL;
void *smb1_backup_rsp_buf = NULL; void *smb1_backup_rsp_buf = NULL;
int rc = 0; int rc = 0;
int tmprc = 0; int tmprc = 0;
@ -960,13 +992,7 @@ cifs_get_inode_info(struct inode **inode,
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
goto out; goto out;
} }
tmp_data = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data,
if (!tmp_data) {
rc = -ENOMEM;
goto out;
}
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
full_path, tmp_data,
&adjust_tz, &is_reparse_point); &adjust_tz, &is_reparse_point);
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
if (rc == -ENOENT && is_tcon_dfs(tcon)) if (rc == -ENOENT && is_tcon_dfs(tcon))
@ -974,7 +1000,7 @@ cifs_get_inode_info(struct inode **inode,
cifs_sb, cifs_sb,
full_path); full_path);
#endif #endif
data = tmp_data; data = &tmp_data;
} }
/* /*
@ -988,14 +1014,24 @@ cifs_get_inode_info(struct inode **inode,
* since we have to check if its reparse tag matches a known * since we have to check if its reparse tag matches a known
* special file type e.g. symlink or fifo or char etc. * special file type e.g. symlink or fifo or char etc.
*/ */
if ((le32_to_cpu(data->Attributes) & ATTR_REPARSE) && if (is_reparse_point && data->symlink_target) {
reparse_tag = IO_REPARSE_TAG_SYMLINK;
} else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) &&
server->ops->query_reparse_tag) { server->ops->query_reparse_tag) {
rc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path,
full_path, &reparse_tag); &reparse_tag);
cifs_dbg(FYI, "reparse tag 0x%x\n", reparse_tag); if (tmprc)
cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc);
if (server->ops->query_symlink) {
tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
&data->symlink_target,
is_reparse_point);
if (tmprc)
cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__,
tmprc);
} }
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, }
is_reparse_point, reparse_tag); cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag);
break; break;
case -EREMOTE: case -EREMOTE:
/* DFS link, no metadata available on this server */ /* DFS link, no metadata available on this server */
@ -1014,18 +1050,20 @@ cifs_get_inode_info(struct inode **inode,
*/ */
if (backup_cred(cifs_sb) && is_smb1_server(server)) { if (backup_cred(cifs_sb) && is_smb1_server(server)) {
/* for easier reading */ /* for easier reading */
FILE_ALL_INFO *fi;
FILE_DIRECTORY_INFO *fdi; FILE_DIRECTORY_INFO *fdi;
SEARCH_ID_FULL_DIR_INFO *si; SEARCH_ID_FULL_DIR_INFO *si;
rc = cifs_backup_query_path_info(xid, tcon, sb, rc = cifs_backup_query_path_info(xid, tcon, sb,
full_path, full_path,
&smb1_backup_rsp_buf, &smb1_backup_rsp_buf,
&data); &fi);
if (rc) if (rc)
goto out; goto out;
fdi = (FILE_DIRECTORY_INFO *)data; move_cifs_info_to_smb2(&data->fi, fi);
si = (SEARCH_ID_FULL_DIR_INFO *)data; fdi = (FILE_DIRECTORY_INFO *)fi;
si = (SEARCH_ID_FULL_DIR_INFO *)fi;
cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb);
fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); fattr.cf_uniqueid = le64_to_cpu(si->UniqueId);
@ -1123,7 +1161,8 @@ handle_mnt_opt:
out: out:
cifs_buf_release(smb1_backup_rsp_buf); cifs_buf_release(smb1_backup_rsp_buf);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
kfree(tmp_data); cifs_free_open_info(&tmp_data);
kfree(fattr.cf_symlink_target);
return rc; return rc;
} }
@ -1138,7 +1177,7 @@ smb311_posix_get_inode_info(struct inode **inode,
bool adjust_tz = false; bool adjust_tz = false;
struct cifs_fattr fattr = {0}; struct cifs_fattr fattr = {0};
bool symlink = false; bool symlink = false;
struct smb311_posix_qinfo *data = NULL; struct cifs_open_info_data data = {};
int rc = 0; int rc = 0;
int tmprc = 0; int tmprc = 0;
@ -1155,15 +1194,9 @@ smb311_posix_get_inode_info(struct inode **inode,
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
goto out; 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, rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data, &adjust_tz,
full_path, data, &symlink);
&adjust_tz, &symlink);
/* /*
* 2. Convert it to internal cifs metadata (fattr) * 2. Convert it to internal cifs metadata (fattr)
@ -1171,7 +1204,7 @@ smb311_posix_get_inode_info(struct inode **inode,
switch (rc) { switch (rc) {
case 0: case 0:
smb311_posix_info_to_fattr(&fattr, data, sb, adjust_tz, symlink); smb311_posix_info_to_fattr(&fattr, &data, sb, adjust_tz, symlink);
break; break;
case -EREMOTE: case -EREMOTE:
/* DFS link, no metadata available on this server */ /* DFS link, no metadata available on this server */
@ -1228,7 +1261,8 @@ smb311_posix_get_inode_info(struct inode **inode,
} }
out: out:
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
kfree(data); cifs_free_open_info(&data);
kfree(fattr.cf_symlink_target);
return rc; return rc;
} }

View File

@ -201,40 +201,6 @@ out:
return rc; return rc;
} }
static int
query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const unsigned char *path,
char **symlinkinfo)
{
int rc;
u8 *buf = NULL;
unsigned int link_len = 0;
unsigned int bytes_read = 0;
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (tcon->ses->server->ops->query_mf_symlink)
rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
cifs_sb, path, buf, &bytes_read);
else
rc = -ENOSYS;
if (rc)
goto out;
if (bytes_read == 0) { /* not a symlink */
rc = -EINVAL;
goto out;
}
rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo);
out:
kfree(buf);
return rc;
}
int int
check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
@ -244,6 +210,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
u8 *buf = NULL; u8 *buf = NULL;
unsigned int link_len = 0; unsigned int link_len = 0;
unsigned int bytes_read = 0; unsigned int bytes_read = 0;
char *symlink = NULL;
if (!couldbe_mf_symlink(fattr)) if (!couldbe_mf_symlink(fattr))
/* it's not a symlink */ /* it's not a symlink */
@ -265,7 +232,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
if (bytes_read == 0) /* not a symlink */ if (bytes_read == 0) /* not a symlink */
goto out; goto out;
rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL); rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink);
if (rc == -EINVAL) { if (rc == -EINVAL) {
/* it's not a symlink */ /* it's not a symlink */
rc = 0; rc = 0;
@ -280,6 +247,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
fattr->cf_mode &= ~S_IFMT; fattr->cf_mode &= ~S_IFMT;
fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
fattr->cf_dtype = DT_LNK; fattr->cf_dtype = DT_LNK;
fattr->cf_symlink_target = symlink;
out: out:
kfree(buf); kfree(buf);
return rc; return rc;
@ -599,75 +567,6 @@ cifs_hl_exit:
return rc; return rc;
} }
const char *
cifs_get_link(struct dentry *direntry, struct inode *inode,
struct delayed_call *done)
{
int rc = -ENOMEM;
unsigned int xid;
const char *full_path;
void *page;
char *target_path = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = NULL;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
if (!direntry)
return ERR_PTR(-ECHILD);
xid = get_xid();
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
free_xid(xid);
return ERR_CAST(tlink);
}
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
page = alloc_dentry_path();
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
free_xid(xid);
cifs_put_tlink(tlink);
free_dentry_path(page);
return ERR_CAST(full_path);
}
cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
rc = -EACCES;
/*
* First try Minshall+French Symlinks, if configured
* and fallback to UNIX Extensions Symlinks.
*/
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
&target_path);
if (rc != 0 && server->ops->query_symlink) {
struct cifsInodeInfo *cifsi = CIFS_I(inode);
bool reparse_point = false;
if (cifsi->cifsAttrs & ATTR_REPARSE)
reparse_point = true;
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
&target_path, reparse_point);
}
free_dentry_path(page);
free_xid(xid);
cifs_put_tlink(tlink);
if (rc != 0) {
kfree(target_path);
return ERR_PTR(rc);
}
set_delayed_call(done, kfree_link, target_path);
return target_path;
}
int int
cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
struct dentry *direntry, const char *symname) struct dentry *direntry, const char *symname)

View File

@ -1011,6 +1011,8 @@ static int cifs_filldir(char *find_entry, struct file *file,
cifs_unix_basic_to_fattr(&fattr, cifs_unix_basic_to_fattr(&fattr,
&((FILE_UNIX_INFO *)find_entry)->basic, &((FILE_UNIX_INFO *)find_entry)->basic,
cifs_sb); cifs_sb);
if (S_ISLNK(fattr.cf_mode))
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
break; break;
case SMB_FIND_FILE_INFO_STANDARD: case SMB_FIND_FILE_INFO_STANDARD:
cifs_std_info_to_fattr(&fattr, cifs_std_info_to_fattr(&fattr,

View File

@ -542,31 +542,32 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink) struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink)
{ {
int rc; int rc;
FILE_ALL_INFO fi = {};
*symlink = false; *symlink = false;
/* could do find first instead but this returns more info */ /* could do find first instead but this returns more info */
rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
cifs_sb->local_nls, cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
/* /*
* BB optimize code so we do not make the above call when server claims * BB optimize code so we do not make the above call when server claims
* no NT SMB support and the above call failed at least once - set flag * no NT SMB support and the above call failed at least once - set flag
* in tcon or mount. * in tcon or mount.
*/ */
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
rc = SMBQueryInformation(xid, tcon, full_path, data, rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_sb->local_nls,
cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
if (!rc)
move_cifs_info_to_smb2(&data->fi, &fi);
*adjustTZ = true; *adjustTZ = true;
} }
if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) { if (!rc && (le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) {
int tmprc; int tmprc;
int oplock = 0; int oplock = 0;
struct cifs_fid fid; struct cifs_fid fid;
@ -592,10 +593,9 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
u64 *uniqueid, FILE_ALL_INFO *data) u64 *uniqueid, struct cifs_open_info_data *unused)
{ {
/* /*
* We can not use the IndexNumber field by default from Windows or * We can not use the IndexNumber field by default from Windows or
@ -613,11 +613,22 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
} }
static int static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
struct cifs_fid *fid, FILE_ALL_INFO *data)
{ {
return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data); int rc;
FILE_ALL_INFO fi = {};
if (cfile->symlink_target) {
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!data->symlink_target)
return -ENOMEM;
}
rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi);
if (!rc)
move_cifs_info_to_smb2(&data->fi, &fi);
return rc;
} }
static void static void
@ -702,19 +713,20 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
cifsInode->cifsAttrs = dosattrs; cifsInode->cifsAttrs = dosattrs;
} }
static int static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, void *buf)
__u32 *oplock, FILE_ALL_INFO *buf)
{ {
FILE_ALL_INFO *fi = buf;
if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
return SMBLegacyOpen(xid, oparms->tcon, oparms->path, return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
oparms->disposition, oparms->disposition,
oparms->desired_access, oparms->desired_access,
oparms->create_options, oparms->create_options,
&oparms->fid->netfid, oplock, buf, &oparms->fid->netfid, oplock, fi,
oparms->cifs_sb->local_nls, oparms->cifs_sb->local_nls,
cifs_remap(oparms->cifs_sb)); cifs_remap(oparms->cifs_sb));
return CIFS_open(xid, oparms, oplock, buf); return CIFS_open(xid, oparms, oplock, fi);
} }
static void static void

View File

@ -20,40 +20,125 @@
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "fscache.h" #include "fscache.h"
#include "smb2proto.h" #include "smb2proto.h"
#include "smb2status.h"
int static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, {
__u32 *oplock, FILE_ALL_INFO *buf) struct smb2_err_rsp *err = iov->iov_base;
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
u32 len;
if (err->ErrorContextCount) {
struct smb2_error_context_rsp *p, *end;
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
ErrorContextData) +
sizeof(struct smb2_symlink_err_rsp));
if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err))
return ERR_PTR(-EINVAL);
p = (struct smb2_error_context_rsp *)err->ErrorData;
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
do {
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData;
break;
}
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len);
} while (p < end);
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
}
if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
sym = ERR_PTR(-EINVAL);
return sym;
}
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path)
{
struct smb2_symlink_err_rsp *sym;
unsigned int sub_offs, sub_len;
unsigned int print_offs, print_len;
char *s;
if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
return -EINVAL;
sym = symlink_data(iov);
if (IS_ERR(sym))
return PTR_ERR(sym);
sub_len = le16_to_cpu(sym->SubstituteNameLength);
sub_offs = le16_to_cpu(sym->SubstituteNameOffset);
print_len = le16_to_cpu(sym->PrintNameLength);
print_offs = le16_to_cpu(sym->PrintNameOffset);
if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
return -EINVAL;
s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true,
cifs_sb->local_nls);
if (!s)
return -ENOMEM;
convert_delimiter(s, '/');
cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s);
*path = s;
return 0;
}
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf)
{ {
int rc; int rc;
__le16 *smb2_path; __le16 *smb2_path;
struct smb2_file_all_info *smb2_data = NULL;
__u8 smb2_oplock; __u8 smb2_oplock;
struct cifs_open_info_data *data = buf;
struct smb2_file_all_info file_info = {};
struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
struct kvec err_iov = {};
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid; struct cifs_fid *fid = oparms->fid;
struct network_resiliency_req nr_ioctl_req; struct network_resiliency_req nr_ioctl_req;
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL) { if (smb2_path == NULL)
rc = -ENOMEM; return -ENOMEM;
goto out;
}
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
GFP_KERNEL);
if (smb2_data == NULL) {
rc = -ENOMEM;
goto out;
}
oparms->desired_access |= FILE_READ_ATTRIBUTES; oparms->desired_access |= FILE_READ_ATTRIBUTES;
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
NULL, NULL); &err_buftype);
if (rc && data) {
struct smb2_hdr *hdr = err_iov.iov_base;
if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER))
rc = -ENOMEM;
else if (hdr->Status == STATUS_STOPPED_ON_SYMLINK && oparms->cifs_sb) {
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
&data->symlink_target);
if (!rc) {
memset(smb2_data, 0, sizeof(*smb2_data));
oparms->create_options |= OPEN_REPARSE_POINT;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
NULL, NULL, NULL);
oparms->create_options &= ~OPEN_REPARSE_POINT;
}
}
}
if (rc) if (rc)
goto out; goto out;
if (oparms->tcon->use_resilient) { if (oparms->tcon->use_resilient) {
/* default timeout is 0, servers pick default (120 seconds) */ /* default timeout is 0, servers pick default (120 seconds) */
nr_ioctl_req.Timeout = nr_ioctl_req.Timeout =
@ -73,7 +158,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = 0; rc = 0;
} }
if (buf) { if (smb2_data) {
/* if open response does not have IndexNumber field - get it */ /* if open response does not have IndexNumber field - get it */
if (smb2_data->IndexNumber == 0) { if (smb2_data->IndexNumber == 0) {
rc = SMB2_get_srv_num(xid, oparms->tcon, rc = SMB2_get_srv_num(xid, oparms->tcon,
@ -89,12 +174,12 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = 0; rc = 0;
} }
} }
move_smb2_info_to_cifs(buf, smb2_data); memcpy(&data->fi, smb2_data, sizeof(data->fi));
} }
*oplock = smb2_oplock; *oplock = smb2_oplock;
out: out:
kfree(smb2_data); free_rsp_buf(err_buftype, err_iov.iov_base);
kfree(smb2_path); kfree(smb2_path);
return rc; return rc;
} }

View File

@ -24,6 +24,7 @@
#include "smb2pdu.h" #include "smb2pdu.h"
#include "smb2proto.h" #include "smb2proto.h"
#include "cached_dir.h" #include "cached_dir.h"
#include "smb2status.h"
static void static void
free_set_inf_compound(struct smb_rqst *rqst) free_set_inf_compound(struct smb_rqst *rqst)
@ -50,13 +51,15 @@ struct cop_vars {
/* /*
* note: If cfile is passed, the reference to it is dropped here. * note: If cfile is passed, the reference to it is dropped here.
* So make sure that you do not reuse cfile after return from this func. * So make sure that you do not reuse cfile after return from this func.
*
* If passing @err_iov and @err_buftype, ensure to make them both large enough (>= 3) to hold all
* error responses. Caller is also responsible for freeing them up.
*/ */
static int static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
__u32 desired_access, __u32 create_disposition, __u32 desired_access, __u32 create_disposition, __u32 create_options,
__u32 create_options, umode_t mode, void *ptr, int command, umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile,
struct cifsFileInfo *cfile) struct kvec *err_iov, int *err_buftype)
{ {
struct cop_vars *vars = NULL; struct cop_vars *vars = NULL;
struct kvec *rsp_iov; struct kvec *rsp_iov;
@ -70,6 +73,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
int num_rqst = 0; int num_rqst = 0;
int resp_buftype[3]; int resp_buftype[3];
struct smb2_query_info_rsp *qi_rsp = NULL; struct smb2_query_info_rsp *qi_rsp = NULL;
struct cifs_open_info_data *idata;
int flags = 0; int flags = 0;
__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
unsigned int size[2]; unsigned int size[2];
@ -385,14 +389,19 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
switch (command) { switch (command) {
case SMB2_OP_QUERY_INFO: case SMB2_OP_QUERY_INFO:
idata = ptr;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
rc = -ENOMEM;
}
if (rc == 0) { if (rc == 0) {
qi_rsp = (struct smb2_query_info_rsp *) qi_rsp = (struct smb2_query_info_rsp *)
rsp_iov[1].iov_base; rsp_iov[1].iov_base;
rc = smb2_validate_and_copy_iov( rc = smb2_validate_and_copy_iov(
le16_to_cpu(qi_rsp->OutputBufferOffset), le16_to_cpu(qi_rsp->OutputBufferOffset),
le32_to_cpu(qi_rsp->OutputBufferLength), le32_to_cpu(qi_rsp->OutputBufferLength),
&rsp_iov[1], sizeof(struct smb2_file_all_info), &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi);
ptr);
} }
if (rqst[1].rq_iov) if (rqst[1].rq_iov)
SMB2_query_info_free(&rqst[1]); SMB2_query_info_free(&rqst[1]);
@ -406,13 +415,19 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
tcon->tid); tcon->tid);
break; break;
case SMB2_OP_POSIX_QUERY_INFO: case SMB2_OP_POSIX_QUERY_INFO:
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
rc = -ENOMEM;
}
if (rc == 0) { if (rc == 0) {
qi_rsp = (struct smb2_query_info_rsp *) qi_rsp = (struct smb2_query_info_rsp *)
rsp_iov[1].iov_base; rsp_iov[1].iov_base;
rc = smb2_validate_and_copy_iov( rc = smb2_validate_and_copy_iov(
le16_to_cpu(qi_rsp->OutputBufferOffset), le16_to_cpu(qi_rsp->OutputBufferOffset),
le32_to_cpu(qi_rsp->OutputBufferLength), le32_to_cpu(qi_rsp->OutputBufferLength),
&rsp_iov[1], sizeof(struct smb311_posix_qinfo) /* add SIDs */, ptr); &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */,
(char *)&idata->posix_fi);
} }
if (rqst[1].rq_iov) if (rqst[1].rq_iov)
SMB2_query_info_free(&rqst[1]); SMB2_query_info_free(&rqst[1]);
@ -477,42 +492,33 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
free_set_inf_compound(rqst); free_set_inf_compound(rqst);
break; break;
} }
if (rc && err_iov && err_buftype) {
memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov));
memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype));
} else {
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
}
kfree(vars); kfree(vars);
return rc; return rc;
} }
void int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
{
memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
dst->CurrentByteOffset = src->CurrentByteOffset;
dst->Mode = src->Mode;
dst->AlignmentRequirement = src->AlignmentRequirement;
dst->IndexNumber1 = 0; /* we don't use it */
}
int
smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
FILE_ALL_INFO *data, bool *adjust_tz, bool *reparse) struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
{ {
int rc; int rc;
struct smb2_file_all_info *smb2_data;
__u32 create_options = 0; __u32 create_options = 0;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL; struct cached_fid *cfid = NULL;
struct kvec err_iov[3] = {};
int err_buftype[3] = {};
*adjust_tz = false; *adjust_tz = false;
*reparse = false; *reparse = false;
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
GFP_KERNEL);
if (smb2_data == NULL)
return -ENOMEM;
if (strcmp(full_path, "")) if (strcmp(full_path, ""))
rc = -ENOENT; rc = -ENOENT;
else else
@ -520,63 +526,58 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
/* If it is a root and its handle is cached then use it */ /* If it is a root and its handle is cached then use it */
if (!rc) { if (!rc) {
if (cfid->file_all_info_is_valid) { if (cfid->file_all_info_is_valid) {
move_smb2_info_to_cifs(data, memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi));
&cfid->file_all_info);
} else { } else {
rc = SMB2_query_info(xid, tcon, rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid,
cfid->fid.persistent_fid, cfid->fid.volatile_fid, &data->fi);
cfid->fid.volatile_fid, smb2_data);
if (!rc)
move_smb2_info_to_cifs(data, smb2_data);
} }
close_cached_dir(cfid); close_cached_dir(cfid);
goto out; return rc;
} }
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile,
ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile); err_iov, err_buftype);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER &&
((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE &&
((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) {
rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target);
if (rc)
goto out;
}
*reparse = true; *reparse = true;
create_options |= OPEN_REPARSE_POINT; create_options |= OPEN_REPARSE_POINT;
/* Failed on a symbolic link - query a reparse point info */ /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_OPEN, create_options, ACL_NO_MODE, data,
create_options, ACL_NO_MODE, SMB2_OP_QUERY_INFO, cfile, NULL, NULL);
smb2_data, SMB2_OP_QUERY_INFO, cfile);
} }
if (rc)
goto out;
move_smb2_info_to_cifs(data, smb2_data);
out: out:
kfree(smb2_data); free_rsp_buf(err_buftype[0], err_iov[0].iov_base);
free_rsp_buf(err_buftype[1], err_iov[1].iov_base);
free_rsp_buf(err_buftype[2], err_iov[2].iov_base);
return rc; return rc;
} }
int int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *reparse) struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
{ {
int rc; int rc;
__u32 create_options = 0; __u32 create_options = 0;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct smb311_posix_qinfo *smb2_data; struct kvec err_iov[3] = {};
int err_buftype[3] = {};
*adjust_tz = false; *adjust_tz = false;
*reparse = false; *reparse = 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. * BB TODO: Add support for using the cached root handle.
* Create SMB2_query_posix_info worker function to do non-compounded query * Create SMB2_query_posix_info worker function to do non-compounded query
@ -585,29 +586,32 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
*/ */
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile,
ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile); err_iov, err_buftype);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
/* BB TODO: When support for special files added to Samba re-verify this path */ /* BB TODO: When support for special files added to Samba re-verify this path */
if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER &&
((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE &&
((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) {
rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target);
if (rc)
goto out;
}
*reparse = true; *reparse = true;
create_options |= OPEN_REPARSE_POINT; create_options |= OPEN_REPARSE_POINT;
/* Failed on a symbolic link - query a reparse point info */ /* Failed on a symbolic link - query a reparse point info */
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_OPEN, create_options, ACL_NO_MODE, data,
create_options, ACL_NO_MODE, SMB2_OP_POSIX_QUERY_INFO, cfile, NULL, NULL);
smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
} }
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: out:
kfree(smb2_data); free_rsp_buf(err_buftype[0], err_iov[0].iov_base);
free_rsp_buf(err_buftype[1], err_iov[1].iov_base);
free_rsp_buf(err_buftype[2], err_iov[2].iov_base);
return rc; return rc;
} }
@ -619,7 +623,7 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
return smb2_compound_op(xid, tcon, cifs_sb, name, return smb2_compound_op(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR,
NULL); NULL, NULL, NULL);
} }
void void
@ -641,7 +645,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, ACL_NO_MODE, CREATE_NOT_FILE, ACL_NO_MODE,
&data, SMB2_OP_SET_INFO, cfile); &data, SMB2_OP_SET_INFO, cfile, NULL, NULL);
if (tmprc == 0) if (tmprc == 0)
cifs_i->cifsAttrs = dosattrs; cifs_i->cifsAttrs = dosattrs;
} }
@ -652,7 +656,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_NOT_FILE, ACL_NO_MODE, CREATE_NOT_FILE, ACL_NO_MODE,
NULL, SMB2_OP_RMDIR, NULL); NULL, SMB2_OP_RMDIR, NULL, NULL, NULL);
} }
int int
@ -661,7 +665,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL); ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL);
} }
static int static int
@ -680,7 +684,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
} }
rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name, FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name,
command, cfile); command, cfile, NULL, NULL);
smb2_rename_path: smb2_rename_path:
kfree(smb2_to_name); kfree(smb2_to_name);
return rc; return rc;
@ -720,7 +724,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
return smb2_compound_op(xid, tcon, cifs_sb, full_path, return smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
&eof, SMB2_OP_SET_EOF, cfile); &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL);
} }
int int
@ -746,7 +750,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, FILE_WRITE_ATTRIBUTES, FILE_OPEN,
0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile); 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile,
NULL, NULL);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return rc; return rc;
} }

View File

@ -831,33 +831,25 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
u64 *uniqueid, FILE_ALL_INFO *data) u64 *uniqueid, struct cifs_open_info_data *data)
{ {
*uniqueid = le64_to_cpu(data->IndexNumber); *uniqueid = le64_to_cpu(data->fi.IndexNumber);
return 0; return 0;
} }
static int static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
struct cifs_fid *fid, FILE_ALL_INFO *data)
{ {
int rc; struct cifs_fid *fid = &cfile->fid;
struct smb2_file_all_info *smb2_data;
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, if (cfile->symlink_target) {
GFP_KERNEL); data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (smb2_data == NULL) if (!data->symlink_target)
return -ENOMEM; return -ENOMEM;
}
rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi);
smb2_data);
if (!rc)
move_smb2_info_to_cifs(data, smb2_data);
kfree(smb2_data);
return rc;
} }
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
@ -2828,9 +2820,6 @@ parse_reparse_point(struct reparse_data_buffer *buf,
} }
} }
#define SMB2_SYMLINK_STRUCT_SIZE \
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
static int static int
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
@ -2842,13 +2831,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
struct cifs_fid fid; struct cifs_fid fid;
struct kvec err_iov = {NULL, 0}; struct kvec err_iov = {NULL, 0};
struct smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink;
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
unsigned int sub_len;
unsigned int sub_offset;
unsigned int print_len;
unsigned int print_offset;
int flags = CIFS_CP_CREATE_CLOSE_OP; int flags = CIFS_CP_CREATE_CLOSE_OP;
struct smb_rqst rqst[3]; struct smb_rqst rqst[3];
int resp_buftype[3]; int resp_buftype[3];
@ -2965,47 +2948,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
goto querty_exit; goto querty_exit;
} }
err_buf = err_iov.iov_base; rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path);
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) {
rc = -EINVAL;
goto querty_exit;
}
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
if (le32_to_cpu(symlink->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
le32_to_cpu(symlink->ReparseTag) != IO_REPARSE_TAG_SYMLINK) {
rc = -EINVAL;
goto querty_exit;
}
/* open must fail on symlink - reset rc */
rc = 0;
sub_len = le16_to_cpu(symlink->SubstituteNameLength);
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
print_len = le16_to_cpu(symlink->PrintNameLength);
print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
rc = -EINVAL;
goto querty_exit;
}
if (err_iov.iov_len <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
rc = -EINVAL;
goto querty_exit;
}
*target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls);
if (!(*target_path)) {
rc = -ENOMEM;
goto querty_exit;
}
convert_delimiter(*target_path, '/');
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
querty_exit: querty_exit:
cifs_dbg(FYI, "query symlink rc %d\n", rc); cifs_dbg(FYI, "query symlink rc %d\n", rc);
@ -5115,7 +5058,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
int rc = -EPERM; int rc = -EPERM;
FILE_ALL_INFO *buf = NULL; struct cifs_open_info_data buf = {};
struct cifs_io_parms io_parms = {0}; struct cifs_io_parms io_parms = {0};
__u32 oplock = 0; __u32 oplock = 0;
struct cifs_fid fid; struct cifs_fid fid;
@ -5131,7 +5074,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
* and was used by default in earlier versions of Windows * and was used by default in earlier versions of Windows
*/ */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
goto out; return rc;
/* /*
* TODO: Add ability to create instead via reparse point. Windows (e.g. * TODO: Add ability to create instead via reparse point. Windows (e.g.
@ -5140,16 +5083,10 @@ smb2_make_node(unsigned int xid, struct inode *inode,
*/ */
if (!S_ISCHR(mode) && !S_ISBLK(mode)) if (!S_ISCHR(mode) && !S_ISBLK(mode))
goto out; return rc;
cifs_dbg(FYI, "sfu compat create special file\n"); cifs_dbg(FYI, "sfu compat create special file\n");
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
}
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb; oparms.cifs_sb = cifs_sb;
oparms.desired_access = GENERIC_WRITE; oparms.desired_access = GENERIC_WRITE;
@ -5164,21 +5101,21 @@ smb2_make_node(unsigned int xid, struct inode *inode,
oplock = REQ_OPLOCK; oplock = REQ_OPLOCK;
else else
oplock = 0; oplock = 0;
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf); rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
if (rc) if (rc)
goto out; return rc;
/* /*
* BB Do not bother to decode buf since no local inode yet to put * BB Do not bother to decode buf since no local inode yet to put
* timestamps in, but we can reuse it safely. * timestamps in, but we can reuse it safely.
*/ */
pdev = (struct win_dev *)buf; pdev = (struct win_dev *)&buf.fi;
io_parms.pid = current->tgid; io_parms.pid = current->tgid;
io_parms.tcon = tcon; io_parms.tcon = tcon;
io_parms.offset = 0; io_parms.offset = 0;
io_parms.length = sizeof(struct win_dev); io_parms.length = sizeof(struct win_dev);
iov[1].iov_base = buf; iov[1].iov_base = &buf.fi;
iov[1].iov_len = sizeof(struct win_dev); iov[1].iov_len = sizeof(struct win_dev);
if (S_ISCHR(mode)) { if (S_ISCHR(mode)) {
memcpy(pdev->type, "IntxCHR", 8); memcpy(pdev->type, "IntxCHR", 8);
@ -5197,8 +5134,8 @@ smb2_make_node(unsigned int xid, struct inode *inode,
d_drop(dentry); d_drop(dentry);
/* FIXME: add code here to set EAs */ /* FIXME: add code here to set EAs */
out:
kfree(buf); cifs_free_open_info(&buf);
return rc; return rc;
} }

View File

@ -56,6 +56,9 @@ struct smb2_rdma_crypto_transform {
#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL #define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL
#define SMB2_SYMLINK_STRUCT_SIZE \
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
#define SYMLINK_ERROR_TAG 0x4c4d5953 #define SYMLINK_ERROR_TAG 0x4c4d5953
struct smb2_symlink_err_rsp { struct smb2_symlink_err_rsp {

View File

@ -53,16 +53,12 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
struct TCP_Server_Info *srv); struct TCP_Server_Info *srv);
extern int smb3_handle_read_data(struct TCP_Server_Info *server, extern int smb3_handle_read_data(struct TCP_Server_Info *server,
struct mid_q_entry *mid); struct mid_q_entry *mid);
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
struct smb2_file_all_info *src);
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *path, struct cifs_sb_info *cifs_sb, const char *path,
__u32 *reparse_tag); __u32 *reparse_tag);
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb, const char *full_path,
const char *full_path, FILE_ALL_INFO *data, struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
bool *adjust_tz, bool *symlink);
extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, __u64 size, const char *full_path, __u64 size,
struct cifs_sb_info *cifs_sb, bool set_alloc); struct cifs_sb_info *cifs_sb, bool set_alloc);
@ -95,9 +91,9 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf, const unsigned char *path, char *pbuf,
unsigned int *pbytes_read); unsigned int *pbytes_read);
extern int smb2_open_file(const unsigned int xid, int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path);
struct cifs_open_parms *oparms, int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
__u32 *oplock, FILE_ALL_INFO *buf); void *buf);
extern int smb2_unlock_range(struct cifsFileInfo *cfile, extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid); struct file_lock *flock, const unsigned int xid);
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
@ -278,9 +274,9 @@ extern int smb2_query_info_compound(const unsigned int xid,
struct kvec *rsp, int *buftype, struct kvec *rsp, int *buftype,
struct cifs_sb_info *cifs_sb); struct cifs_sb_info *cifs_sb);
/* query path info from the server using SMB311 POSIX extensions*/ /* 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, 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, struct cifs_sb_info *cifs_sb, const char *full_path,
bool *adjust_tx, bool *symlink); struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
int posix_info_parse(const void *beg, const void *end, int posix_info_parse(const void *beg, const void *end,
struct smb2_posix_info_parsed *out); struct smb2_posix_info_parsed *out);
int posix_info_sid_size(const void *beg, const void *end); int posix_info_sid_size(const void *beg, const void *end);