Skip to content

Commit

Permalink
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Browse files Browse the repository at this point in the history
Pull CIFS fixes from Steve French:
 "Small fix from Jeff for writepages leak, and some fixes for ACLs and
  xattrs when SMB2 enabled.

  Am expecting another fix from Jeff and at least one more fix (for
  mounting SMB2 with cifsacl) in the next week"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6:
  [CIFS] clean up page array when uncached write send fails
  cifs: use a flexarray in cifs_writedata
  retrieving CIFS ACLs when mounted with SMB2 fails dropping session
  Add protocol specific operation for CIFS xattrs
  • Loading branch information
Linus Torvalds committed Feb 10, 2014
2 parents b28a960 + 4a5c80d commit cbf2822
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 39 deletions.
28 changes: 24 additions & 4 deletions fs/cifs/cifsacl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1043,15 +1043,30 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
__u32 secdesclen = 0;
struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
struct cifs_tcon *tcon;

if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);

cifs_dbg(NOISY, "set ACL from mode for %s\n", path);

/* Get the security descriptor */
pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);

if (tcon->ses->server->ops->get_acl == NULL) {
cifs_put_tlink(tlink);
return -EOPNOTSUPP;
}

pntsd = tcon->ses->server->ops->get_acl(cifs_sb, inode, path,
&secdesclen);
if (IS_ERR(pntsd)) {
rc = PTR_ERR(pntsd);
cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
goto out;
cifs_put_tlink(tlink);
return rc;
}

/*
Expand All @@ -1064,6 +1079,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
pnntsd = kmalloc(secdesclen, GFP_KERNEL);
if (!pnntsd) {
kfree(pntsd);
cifs_put_tlink(tlink);
return -ENOMEM;
}

Expand All @@ -1072,14 +1088,18 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,

cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);

if (tcon->ses->server->ops->set_acl == NULL)
rc = -EOPNOTSUPP;

if (!rc) {
/* Set the security descriptor */
rc = set_cifs_acl(pnntsd, secdesclen, inode, path, aclflag);
rc = tcon->ses->server->ops->set_acl(pnntsd, secdesclen, inode,
path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
}
cifs_put_tlink(tlink);

kfree(pnntsd);
kfree(pntsd);
out:
return rc;
}
9 changes: 7 additions & 2 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ struct smb_version_operations {
/* async read from the server */
int (*async_readv)(struct cifs_readdata *);
/* async write to the server */
int (*async_writev)(struct cifs_writedata *);
int (*async_writev)(struct cifs_writedata *,
void (*release)(struct kref *));
/* sync read from the server */
int (*sync_read)(const unsigned int, struct cifsFileInfo *,
struct cifs_io_parms *, unsigned int *, char **,
Expand Down Expand Up @@ -395,6 +396,10 @@ struct smb_version_operations {
int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *,
const char *, const void *, const __u16,
const struct nls_table *, int);
struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *,
const char *, u32 *);
int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *,
int);
};

struct smb_version_values {
Expand Down Expand Up @@ -1064,7 +1069,7 @@ struct cifs_writedata {
unsigned int pagesz;
unsigned int tailsz;
unsigned int nr_pages;
struct page *pages[1];
struct page *pages[];
};

/*
Expand Down
3 changes: 2 additions & 1 deletion fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,8 @@ void cifs_readdata_release(struct kref *refcount);
int cifs_async_readv(struct cifs_readdata *rdata);
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);

int cifs_async_writev(struct cifs_writedata *wdata);
int cifs_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref));
void cifs_writev_complete(struct work_struct *work);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
work_func_t complete);
Expand Down
15 changes: 5 additions & 10 deletions fs/cifs/cifssmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1910,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)

do {
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata);
rc = server->ops->async_writev(wdata, cifs_writedata_release);
} while (rc == -EAGAIN);

for (i = 0; i < wdata->nr_pages; i++) {
Expand Down Expand Up @@ -1962,15 +1962,9 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
{
struct cifs_writedata *wdata;

/* this would overflow */
if (nr_pages == 0) {
cifs_dbg(VFS, "%s: called with nr_pages == 0!\n", __func__);
return NULL;
}

/* writedata + number of page pointers */
wdata = kzalloc(sizeof(*wdata) +
sizeof(struct page *) * (nr_pages - 1), GFP_NOFS);
sizeof(struct page *) * nr_pages, GFP_NOFS);
if (wdata != NULL) {
kref_init(&wdata->refcount);
INIT_LIST_HEAD(&wdata->list);
Expand Down Expand Up @@ -2031,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid)

/* cifs_async_writev - send an async write, and set up mid to handle result */
int
cifs_async_writev(struct cifs_writedata *wdata)
cifs_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref))
{
int rc = -EACCES;
WRITE_REQ *smb = NULL;
Expand Down Expand Up @@ -2105,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
if (rc == 0)
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
else
kref_put(&wdata->refcount, cifs_writedata_release);
kref_put(&wdata->refcount, release);

async_writev_out:
cifs_small_buf_release(smb);
Expand Down
31 changes: 20 additions & 11 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -2043,7 +2043,8 @@ static int cifs_writepages(struct address_space *mapping,
}
wdata->pid = wdata->cfile->pid;
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata);
rc = server->ops->async_writev(wdata,
cifs_writedata_release);
} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);

for (i = 0; i < nr_pages; ++i)
Expand Down Expand Up @@ -2331,9 +2332,20 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
}

static void
cifs_uncached_writev_complete(struct work_struct *work)
cifs_uncached_writedata_release(struct kref *refcount)
{
int i;
struct cifs_writedata *wdata = container_of(refcount,
struct cifs_writedata, refcount);

for (i = 0; i < wdata->nr_pages; i++)
put_page(wdata->pages[i]);
cifs_writedata_release(refcount);
}

static void
cifs_uncached_writev_complete(struct work_struct *work)
{
struct cifs_writedata *wdata = container_of(work,
struct cifs_writedata, work);
struct inode *inode = wdata->cfile->dentry->d_inode;
Expand All @@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work)

complete(&wdata->done);

if (wdata->result != -EAGAIN) {
for (i = 0; i < wdata->nr_pages; i++)
put_page(wdata->pages[i]);
}

kref_put(&wdata->refcount, cifs_writedata_release);
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
}

/* attempt to send write to server, retry on any -EAGAIN errors */
Expand All @@ -2370,7 +2377,8 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
if (rc != 0)
continue;
}
rc = server->ops->async_writev(wdata);
rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release);
} while (rc == -EAGAIN);

return rc;
Expand Down Expand Up @@ -2454,7 +2462,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
rc = cifs_uncached_retry_writev(wdata);
if (rc) {
kref_put(&wdata->refcount, cifs_writedata_release);
kref_put(&wdata->refcount,
cifs_uncached_writedata_release);
break;
}

Expand Down Expand Up @@ -2496,7 +2505,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
}
}
list_del_init(&wdata->list);
kref_put(&wdata->refcount, cifs_writedata_release);
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
}

if (total_written > 0)
Expand Down
13 changes: 9 additions & 4 deletions fs/cifs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,10 +527,15 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);

rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS",
ea_value, 4 /* size of buf */, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (tcon->ses->server->ops->query_all_EAs == NULL) {
cifs_put_tlink(tlink);
return -EOPNOTSUPP;
}

rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path,
"SETFILEBITS", ea_value, 4 /* size of buf */,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_put_tlink(tlink);
if (rc < 0)
return (int)rc;
Expand Down
8 changes: 8 additions & 0 deletions fs/cifs/smb1ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,14 @@ struct smb_version_operations smb1_operations = {
.query_mf_symlink = cifs_query_mf_symlink,
.create_mf_symlink = cifs_create_mf_symlink,
.is_read_op = cifs_is_read_op,
#ifdef CONFIG_CIFS_XATTR
.query_all_EAs = CIFSSMBQAllEAs,
.set_EA = CIFSSMBSetEA,
#endif /* CIFS_XATTR */
#ifdef CONFIG_CIFS_ACL
.get_acl = get_cifs_acl,
.set_acl = set_cifs_acl,
#endif /* CIFS_ACL */
};

struct smb_version_values smb1_values = {
Expand Down
5 changes: 3 additions & 2 deletions fs/cifs/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1890,7 +1890,8 @@ smb2_writev_callback(struct mid_q_entry *mid)

/* smb2_async_writev - send an async write, and set up mid to handle result */
int
smb2_async_writev(struct cifs_writedata *wdata)
smb2_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref))
{
int rc = -EACCES;
struct smb2_write_req *req = NULL;
Expand Down Expand Up @@ -1938,7 +1939,7 @@ smb2_async_writev(struct cifs_writedata *wdata)
smb2_writev_callback, wdata, 0);

if (rc) {
kref_put(&wdata->refcount, cifs_writedata_release);
kref_put(&wdata->refcount, release);
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
}

Expand Down
3 changes: 2 additions & 1 deletion fs/cifs/smb2proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
extern int smb2_async_readv(struct cifs_readdata *rdata);
extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, char **buf, int *buf_type);
extern int smb2_async_writev(struct cifs_writedata *wdata);
extern int smb2_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref));
extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, struct kvec *iov, int n_vec);
extern int SMB2_echo(struct TCP_Server_Info *server);
Expand Down
15 changes: 11 additions & 4 deletions fs/cifs/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,12 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
rc = -ENOMEM;
} else {
memcpy(pacl, ea_value, value_size);
rc = set_cifs_acl(pacl, value_size,
direntry->d_inode, full_path, CIFS_ACL_DACL);
if (pTcon->ses->server->ops->set_acl)
rc = pTcon->ses->server->ops->set_acl(pacl,
value_size, direntry->d_inode,
full_path, CIFS_ACL_DACL);
else
rc = -EOPNOTSUPP;
if (rc == 0) /* force revalidate of the inode */
CIFS_I(direntry->d_inode)->time = 0;
kfree(pacl);
Expand Down Expand Up @@ -323,8 +327,11 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
u32 acllen;
struct cifs_ntsd *pacl;

pacl = get_cifs_acl(cifs_sb, direntry->d_inode,
full_path, &acllen);
if (pTcon->ses->server->ops->get_acl == NULL)
goto get_ea_exit; /* rc already EOPNOTSUPP */

pacl = pTcon->ses->server->ops->get_acl(cifs_sb,
direntry->d_inode, full_path, &acllen);
if (IS_ERR(pacl)) {
rc = PTR_ERR(pacl);
cifs_dbg(VFS, "%s: error %zd getting sec desc\n",
Expand Down

0 comments on commit cbf2822

Please sign in to comment.