Skip to content

Commit

Permalink
Do not attempt to do cifs operations reading symlinks with SMB2
Browse files Browse the repository at this point in the history
When use of symlinks is enabled (mounting with mfsymlinks option) to
non-Samba servers, we always tried to use cifs, even when we
were mounted with SMB2 or SMB3, which causes the server to drop the
network connection.

This patch separates out the protocol specific operations for cifs from
the code which recognizes symlinks, and fixes the problem where
with SMB2 mounts we attempt cifs operations to open and read
symlinks.  The next patch will add support for SMB2 for opening
and reading symlinks.  Additional followon patches will address
the similar problem creating symlinks.

Signed-off-by: Steve French <smfrench@gmail.com>
  • Loading branch information
Steve French committed Jul 31, 2013
1 parent 057d633 commit 1b24408
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 32 deletions.
3 changes: 3 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ struct smb_version_operations {
void (*generate_signingkey)(struct TCP_Server_Info *server);
int (*calc_signature)(struct smb_rqst *rqst,
struct TCP_Server_Info *server);
int (*query_mf_symlink)(const unsigned char *path, char *pbuf,
unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
unsigned int xid);
};

struct smb_version_values {
Expand Down
4 changes: 3 additions & 1 deletion fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,5 +497,7 @@ void cifs_writev_complete(struct work_struct *work);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
work_func_t complete);
void cifs_writedata_release(struct kref *refcount);

int open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
unsigned int xid);
#endif /* _CIFSPROTO_H */
84 changes: 53 additions & 31 deletions fs/cifs/link.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,67 +305,89 @@ CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
}

int
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, unsigned int xid)
open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
unsigned int xid)
{
int rc;
int oplock = 0;
__u16 netfid = 0;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
struct cifs_tcon *ptcon;
struct cifs_io_parms io_parms;
u8 *buf;
char *pbuf;
unsigned int bytes_read = 0;
int buf_type = CIFS_NO_BUFFER;
unsigned int link_len = 0;
FILE_ALL_INFO file_info;

if (!CIFSCouldBeMFSymlink(fattr))
/* it's not a symlink */
return 0;

tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
ptcon = tlink_tcon(tlink);

rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
rc = CIFSSMBOpen(xid, ptcon, path, FILE_OPEN, GENERIC_READ,
CREATE_NOT_DIR, &netfid, &oplock, &file_info,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != 0)
goto out;
if (rc != 0) {
cifs_put_tlink(tlink);
return rc;
}

if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
CIFSSMBClose(xid, pTcon, netfid);
CIFSSMBClose(xid, ptcon, netfid);
cifs_put_tlink(tlink);
/* it's not a symlink */
goto out;
return rc;
}

buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}
pbuf = buf;
io_parms.netfid = netfid;
io_parms.pid = current->tgid;
io_parms.tcon = pTcon;
io_parms.tcon = ptcon;
io_parms.offset = 0;
io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;

rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type);
CIFSSMBClose(xid, pTcon, netfid);
if (rc != 0) {
kfree(buf);
rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
CIFSSMBClose(xid, ptcon, netfid);
cifs_put_tlink(tlink);
return rc;
}


int
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, unsigned int xid)
{
int rc = 0;
u8 *buf = NULL;
unsigned int link_len = 0;
unsigned int bytes_read = 0;
struct cifs_tcon *ptcon;

if (!CIFSCouldBeMFSymlink(fattr))
/* it's not a symlink */
return 0;

buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}

ptcon = tlink_tcon(cifs_sb_tlink(cifs_sb));
if ((ptcon->ses) && (ptcon->ses->server->ops->query_mf_symlink))
rc = ptcon->ses->server->ops->query_mf_symlink(path, buf,
&bytes_read, cifs_sb, xid);
else
goto out;

if (rc != 0)
goto out;

if (bytes_read == 0) /* not a symlink */
goto out;

rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
kfree(buf);
if (rc == -EINVAL) {
/* it's not a symlink */
rc = 0;
Expand All @@ -381,7 +403,7 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr,
fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
fattr->cf_dtype = DT_LNK;
out:
cifs_put_tlink(tlink);
kfree(buf);
return rc;
}

Expand Down
1 change: 1 addition & 0 deletions fs/cifs/smb1ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ struct smb_version_operations smb1_operations = {
.mand_lock = cifs_mand_lock,
.mand_unlock_range = cifs_unlock_range,
.push_mand_locks = cifs_push_mandatory_locks,
.query_mf_symlink = open_query_close_cifs_symlink,
};

struct smb_version_values smb1_values = {
Expand Down

0 comments on commit 1b24408

Please sign in to comment.