On Sun, 8 Dec 2013 14:13:37 -0700 Tim Gardner <timg@xxxxxxx> wrote: > As suggested by Jeff Layton: > > -------- > "Now that I look though, it's clear to me that cifs_set_file_size is > just wrong. Currently it calls ops->set_path_size and if that fails > with certain error codes it tries to do a SMBLegacyOpen, etc. That's > going to fall on its face if this is a SMB2/3 connection. > > Here's what should be done instead, I think: > > - get rid of both set_path_size and set_file_size operations. The > version specific abstraction was implemented at the wrong level, IMO. > > - implement a new protocol level operation that basically takes the > same args as cifs_set_file_size. cifs_setattr_unix/_nounix should > call this operation. > > - the set_path_size operation for SMB1 should basically be the function > that is cifs_set_file_size today (though that should probably be > renamed). That function should be restructured to do the following: > > + look for an open filehandle > + if there isn't one, open the file > + call CIFSSMBSetFileSize > + fall back to zero-length write if that fails > + close the file if we opened it" > -------- > > This patch also moves all of the SMBv1 legacy hacks from cifs_set_file_size() into > the SMBv1 specific handler smb_set_file_size() more or less intact. SMBv2 and higher are > more regular and orthogonal, so the v2 handler smb2_set_file_size() no longer contains > the legacy hacks. > > Cc: Steve French <sfrench@xxxxxxxxx> > Cc: Jeff Layton <jlayton@xxxxxxxxxx> > Cc: Dean Gehnert <deang@xxxxxxx> > Signed-off-by: Tim Gardner <timg@xxxxxxx> > --- > > V2 - this is a new patch in the V2 series. > > I know this is kind of a giant patch, but there really doesn't seem to be any > intermediate steps. Its pretty much all or nothing. > > I've only tested against a Linux CIFS server running a 3.2 kernel since I no longer > have easy access to smbv2 servers (iOS 10.8 or Windows 7). > > resent with corrected Cc's > > rtg > > fs/cifs/cifsglob.h | 9 ++--- > fs/cifs/inode.c | 108 +++++---------------------------------------------- > fs/cifs/smb1ops.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++- > fs/cifs/smb2ops.c | 57 ++++++++++++++++++++++++--- > 4 files changed, 170 insertions(+), 113 deletions(-) > > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > index 8fd8eb2..6113ce8 100644 > --- a/fs/cifs/cifsglob.h > +++ b/fs/cifs/cifsglob.h > @@ -269,12 +269,9 @@ struct smb_version_operations { > int (*get_srv_inum)(const unsigned int, struct cifs_tcon *, > struct cifs_sb_info *, const char *, > u64 *uniqueid, FILE_ALL_INFO *); > - /* set size by path */ > - int (*set_file_size_by_path)(const unsigned int, struct cifs_tcon *, > - const char *, __u64, struct cifs_sb_info *, bool); > - /* set size by file handle */ > - int (*set_file_size_by_handle)(const unsigned int, struct cifs_tcon *, > - struct cifsFileInfo *, __u64, bool); > + /* set file size */ > + int (*set_file_size)(struct inode *inode, struct iattr *attrs, > + unsigned int xid, char *full_path); > /* set attributes */ > int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, > const unsigned int); > diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c > index e332038..3edeafd 100644 > --- a/fs/cifs/inode.c > +++ b/fs/cifs/inode.c > @@ -1899,118 +1899,28 @@ static void cifs_setsize(struct inode *inode, loff_t offset) > truncate_pagecache(inode, offset); > } > > -/* > - * Legacy hack to set file length. > - */ > -static int > -cifs_legacy_set_file_size(unsigned int xid, __u16 netfid, __u32 pid, > - unsigned int length, struct cifs_tcon *tcon, > - char *full_path) > -{ > - int rc; > - unsigned int bytes_written; > - struct cifs_io_parms io_parms; > - > - io_parms.netfid = netfid; > - io_parms.pid = pid; > - io_parms.tcon = tcon; > - io_parms.offset = 0; > - io_parms.length = length; > - rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, > - NULL, NULL, 1); > - cifs_dbg(FYI, "%s len %d rc %d on %s\n", __func__, length, rc, > - full_path); > - return rc; > -} > - > static int > cifs_set_file_size(struct inode *inode, struct iattr *attrs, > unsigned int xid, char *full_path) > { > - int rc; > - struct cifsFileInfo *open_file; > + int rc = -ENOSYS; > struct cifsInodeInfo *cifsInode = CIFS_I(inode); > struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); > struct tcon_link *tlink = NULL; > struct cifs_tcon *tcon = NULL; > struct TCP_Server_Info *server; > > - /* > - * To avoid spurious oplock breaks from server, in the case of > - * inodes that we already have open, avoid doing path based > - * setting of file size if we can do it by handle. > - * This keeps our caching token (oplock) and avoids timeouts > - * when the local oplock break takes longer to flush > - * writebehind data than the SMB timeout for the SetPathInfo > - * request would allow > - */ > - open_file = find_writable_file(cifsInode, true); > - if (open_file) { > - tcon = tlink_tcon(open_file->tlink); > - server = tcon->ses->server; > - if (server->ops->set_file_size_by_handle) > - rc = server->ops->set_file_size_by_handle(xid, tcon, > - open_file, > - attrs->ia_size, > - false); > - else > - rc = -ENOSYS; > - cifsFileInfo_put(open_file); > - cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); > - if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { > - rc = cifs_legacy_set_file_size(xid, > - open_file->fid.netfid, > - open_file->pid, > - attrs->ia_size, > - tcon, full_path); > - } > - } else > - rc = -EINVAL; > - > - if (!rc) > - goto set_size_out; > + tlink = cifs_sb_tlink(cifs_sb); > + if (IS_ERR(tlink)) > + return PTR_ERR(tlink); > + tcon = tlink_tcon(tlink); > + server = tcon->ses->server; > > - if (tcon == NULL) { > - tlink = cifs_sb_tlink(cifs_sb); > - if (IS_ERR(tlink)) > - return PTR_ERR(tlink); > - tcon = tlink_tcon(tlink); > - server = tcon->ses->server; > - } > + if (server->ops->set_file_size) > + rc = server->ops->set_file_size(inode, attrs, xid, full_path); > > - /* > - * Set file size by pathname rather than by handle either because no > - * valid, writeable file handle for it was found or because there was > - * an error setting it by handle. > - */ > - if (server->ops->set_file_size_by_path) > - rc = server->ops->set_file_size_by_path(xid, tcon, full_path, > - attrs->ia_size, cifs_sb, > - false); > - else > - rc = -ENOSYS; > - cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); > - if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { > - __u16 netfid; > - int oplock = 0; > - > - rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN, > - GENERIC_WRITE, CREATE_NOT_DIR, &netfid, > - &oplock, NULL, cifs_sb->local_nls, > - cifs_sb->mnt_cifs_flags & > - CIFS_MOUNT_MAP_SPECIAL_CHR); > - if (rc == 0) { > - rc = cifs_legacy_set_file_size(xid, netfid, > - current->tgid, > - attrs->ia_size, tcon, > - full_path); > - CIFSSMBClose(xid, tcon, netfid); > - } > - } > - if (!open_file) > - cifs_put_tlink(tlink); > + cifs_put_tlink(tlink); > > -set_size_out: > if (rc == 0) { > cifsInode->server_eof = attrs->ia_size; > cifs_setsize(inode, attrs->ia_size); > diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c > index c5d9ec6..63ab3aa 100644 > --- a/fs/cifs/smb1ops.c > +++ b/fs/cifs/smb1ops.c > @@ -755,6 +755,30 @@ cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile, > return CIFSSMBWrite2(xid, parms, written, iov, nr_segs); > } > > +/* > + * Legacy hack to set file length. > + */ > +static int > +cifs_legacy_set_file_size(unsigned int xid, __u16 netfid, __u32 pid, > + unsigned int length, struct cifs_tcon *tcon, > + char *full_path) > +{ > + int rc; > + unsigned int bytes_written; > + struct cifs_io_parms io_parms; > + > + io_parms.netfid = netfid; > + io_parms.pid = pid; > + io_parms.tcon = tcon; > + io_parms.offset = 0; > + io_parms.length = length; > + rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, > + NULL, NULL, 1); > + cifs_dbg(FYI, "%s len %d rc %d on %s\n", __func__, length, rc, > + full_path); > + return rc; > +} > + > static int > smb_set_file_size_by_path(const unsigned int xid, struct cifs_tcon *tcon, > const char *full_path, __u64 size, struct cifs_sb_info *cifs_sb, > @@ -790,6 +814,88 @@ smb_set_file_size_by_path(const unsigned int xid, struct cifs_tcon *tcon, > } > > static int > +smb_set_file_size(struct inode *inode, struct iattr *attrs, unsigned int xid, > + char *full_path) > +{ > + int rc; > + struct cifsFileInfo *open_file; > + struct cifsInodeInfo *cifsInode = CIFS_I(inode); > + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); > + struct tcon_link *tlink = NULL; > + struct cifs_tcon *tcon = NULL; > + struct TCP_Server_Info *server; > + > + /* > + * To avoid spurious oplock breaks from server, in the case of > + * inodes that we already have open, avoid doing path based > + * setting of file size if we can do it by handle. > + * This keeps our caching token (oplock) and avoids timeouts > + * when the local oplock break takes longer to flush > + * writebehind data than the SMB timeout for the SetPathInfo > + * request would allow > + */ > + open_file = find_writable_file(cifsInode, true); > + if (open_file) { > + tcon = tlink_tcon(open_file->tlink); > + server = tcon->ses->server; > + rc = CIFSSMBSetFileSize(xid, tcon, open_file, attrs->ia_size, > + false); > + cifsFileInfo_put(open_file); > + cifs_dbg(FYI, "%s by handle rc = %d for %s\n", __func__, rc, > + full_path); > + if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) > + rc = cifs_legacy_set_file_size(xid, > + open_file->fid.netfid, > + open_file->pid, > + attrs->ia_size, > + tcon, full_path); Ouch, there's an existing bug here that's getting copy and pasted... We're calling find_writeable_file to get a reference to open_file. Then we call cifsFileInfo_put to put that reference, and then we dereference that pointer to pass to cifs_legacy_set_file_size. That cifsFileInfo_put needs to be moved down to after the cifs_legacy_set_file_size call. Can you fix that while you're in here? > + } else > + rc = -EINVAL; > + > + if (!rc) > + goto set_size_out; > + > + if (tcon == NULL) { > + tlink = cifs_sb_tlink(cifs_sb); > + if (IS_ERR(tlink)) > + return PTR_ERR(tlink); > + tcon = tlink_tcon(tlink); > + server = tcon->ses->server; > + } > + > + /* > + * Set file size by pathname rather than by handle either because no > + * valid, writeable file handle for it was found or because there was > + * an error setting it by handle. > + */ > + rc = smb_set_file_size_by_path(xid, tcon, full_path, attrs->ia_size, > + cifs_sb, false); > + cifs_dbg(FYI, "%s by path rc = %d for %s\n", __func__, rc, full_path); > + if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { > + __u16 netfid; > + int oplock = 0; > + > + rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN, > + GENERIC_WRITE, CREATE_NOT_DIR, &netfid, > + &oplock, NULL, cifs_sb->local_nls, > + cifs_sb->mnt_cifs_flags & > + CIFS_MOUNT_MAP_SPECIAL_CHR); > + if (rc == 0) { > + rc = cifs_legacy_set_file_size(xid, netfid, > + current->tgid, > + attrs->ia_size, tcon, > + full_path); > + CIFSSMBClose(xid, tcon, netfid); > + } > + } > + if (!open_file) > + cifs_put_tlink(tlink); > + > +set_size_out: > + return rc; > +} > + > +static int > smb_set_file_info(struct inode *inode, const char *full_path, > FILE_BASIC_INFO *buf, const unsigned int xid) > { > @@ -1013,8 +1119,7 @@ struct smb_version_operations smb1_operations = { > .query_path_info = cifs_query_path_info, > .query_file_info = cifs_query_file_info, > .get_srv_inum = cifs_get_srv_inum, > - .set_file_size_by_path = smb_set_file_size_by_path, > - .set_file_size_by_handle = CIFSSMBSetFileSize, > + .set_file_size = smb_set_file_size, > .set_file_info = smb_set_file_info, > .set_compression = cifs_set_compression, > .echo = CIFSSMBEcho, > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > index dc7b1cb..206b45f 100644 > --- a/fs/cifs/smb2ops.c > +++ b/fs/cifs/smb2ops.c > @@ -697,6 +697,54 @@ smb2_set_file_size_by_handle(const unsigned int xid, struct cifs_tcon *tcon, > } > > static int > +smb2_set_file_size(struct inode *inode, struct iattr *attrs, unsigned int xid, > + char *full_path) > +{ > + int rc; > + struct cifsFileInfo *open_file; > + struct cifsInodeInfo *cifsInode = CIFS_I(inode); > + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); > + struct tcon_link *tlink; > + struct cifs_tcon *tcon; > + struct TCP_Server_Info *server; > + > + /* > + * To avoid spurious oplock breaks from server, in the case of > + * inodes that we already have open, avoid doing path based > + * setting of file size if we can do it by handle. > + * This keeps our caching token (oplock) and avoids timeouts > + * when the local oplock break takes longer to flush > + * writebehind data than the SMB timeout for the SetPathInfo > + * request would allow > + */ > + open_file = find_writable_file(cifsInode, true); > + if (open_file) { > + tcon = tlink_tcon(open_file->tlink); > + server = tcon->ses->server; > + rc = smb2_set_file_size_by_handle(xid, tcon, open_file, > + attrs->ia_size, false); > + cifsFileInfo_put(open_file); > + cifs_dbg(FYI, "%s by handle rc = %d on %s\n", __func__, rc, > + full_path); > + return rc; > + } > + > + tlink = cifs_sb_tlink(cifs_sb); > + if (IS_ERR(tlink)) > + return PTR_ERR(tlink); > + tcon = tlink_tcon(tlink); > + server = tcon->ses->server; > + > + rc = smb2_set_file_size_by_path(xid, tcon, full_path, attrs->ia_size, > + cifs_sb, false); > + cifs_dbg(FYI, "%s by path rc = %d on %s\n", __func__, rc, full_path); > + > + cifs_put_tlink(tlink); > + > + return rc; > +} > + > +static int > smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, > struct cifsFileInfo *cfile) > { > @@ -1131,8 +1179,7 @@ struct smb_version_operations smb20_operations = { > .query_path_info = smb2_query_path_info, > .get_srv_inum = smb2_get_srv_inum, > .query_file_info = smb2_query_file_info, > - .set_file_size_by_path = smb2_set_file_size_by_path, > - .set_file_size_by_handle = smb2_set_file_size_by_handle, > + .set_file_size = smb2_set_file_size, > .set_file_info = smb2_set_file_info, > .set_compression = smb2_set_compression, > .mkdir = smb2_mkdir, > @@ -1205,8 +1252,7 @@ struct smb_version_operations smb21_operations = { > .query_path_info = smb2_query_path_info, > .get_srv_inum = smb2_get_srv_inum, > .query_file_info = smb2_query_file_info, > - .set_file_size_by_path = smb2_set_file_size_by_path, > - .set_file_size_by_handle = smb2_set_file_size_by_handle, > + .set_file_size = smb2_set_file_size, > .set_file_info = smb2_set_file_info, > .set_compression = smb2_set_compression, > .mkdir = smb2_mkdir, > @@ -1280,8 +1326,7 @@ struct smb_version_operations smb30_operations = { > .query_path_info = smb2_query_path_info, > .get_srv_inum = smb2_get_srv_inum, > .query_file_info = smb2_query_file_info, > - .set_file_size_by_path = smb2_set_file_size_by_path, > - .set_file_size_by_handle = smb2_set_file_size_by_handle, > + .set_file_size = smb2_set_file_size, > .set_file_info = smb2_set_file_info, > .set_compression = smb2_set_compression, > .mkdir = smb2_mkdir, The rest of the patch looks good to me though. -- Jeff Layton <jlayton@xxxxxxxxxx> -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html