From df2cf170c823ba779ca339e3ede347c87f4dc6a9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 12 Feb 2010 07:44:16 -0500 Subject: cifs: overhaul cifs_revalidate and rename to cifs_revalidate_dentry cifs_revalidate is renamed to cifs_revalidate_dentry as a later patch will add a by-filehandle variant. Add a new "invalid_mapping" flag to the cifsInodeInfo that indicates that the pagecache is considered invalid. Add a new routine to check inode attributes whenever they're updated and set that flag if the inode has changed on the server. cifs_revalidate_dentry is then changed to just update the attrcache if needed and then to zap the pagecache if it's not valid. There are some other behavior changes in here as well. Open files are now allowed to have their caches invalidated. I see no reason why we'd want to keep stale data around just because a file is open. Also, cifs_revalidate_cache uses the server_eof for revalidating the file size since that should more closely match the size of the file on the server. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 3 +- fs/cifs/cifsfs.h | 2 +- fs/cifs/cifsglob.h | 1 + fs/cifs/dir.c | 2 +- fs/cifs/file.c | 2 +- fs/cifs/inode.c | 211 +++++++++++++++++++++++++---------------------------- 6 files changed, 104 insertions(+), 117 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8c6a0362717..cf85a4165b0 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -312,6 +312,7 @@ cifs_alloc_inode(struct super_block *sb) cifs_inode->clientCanCacheRead = false; cifs_inode->clientCanCacheAll = false; cifs_inode->delete_pending = false; + cifs_inode->invalid_mapping = false; cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ cifs_inode->server_eof = 0; @@ -638,7 +639,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) setting the revalidate time to zero */ CIFS_I(file->f_path.dentry->d_inode)->time = 0; - retval = cifs_revalidate(file->f_path.dentry); + retval = cifs_revalidate_dentry(file->f_path.dentry); if (retval < 0) return (loff_t)retval; } diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 78c1b86d55f..2af995ca895 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -61,7 +61,7 @@ extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); -extern int cifs_revalidate(struct dentry *); +extern int cifs_revalidate_dentry(struct dentry *); extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_setattr(struct dentry *, struct iattr *); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a1c817eb291..63c89d1d70b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -389,6 +389,7 @@ struct cifsInodeInfo { bool clientCanCacheRead:1; /* read oplock */ bool clientCanCacheAll:1; /* read and writebehind oplock */ bool delete_pending:1; /* DELETE_ON_CLOSE is set */ + bool invalid_mapping:1; /* pagecache is invalid */ u64 server_eof; /* current file size on server */ u64 uniqueid; /* server inode number */ struct inode vfs_inode; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 6ccf7262d1b..e9f7ecc2714 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -739,7 +739,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) int isValid = 1; if (direntry->d_inode) { - if (cifs_revalidate(direntry)) + if (cifs_revalidate_dentry(direntry)) return 0; } else { cFYI(1, ("neg dentry 0x%p name = %s", diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 3d8f8a96f5a..b90f8f2ca85 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1894,7 +1894,7 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) int rc, xid; xid = GetXid(); - rc = cifs_revalidate(dentry); + rc = cifs_revalidate_dentry(dentry); if (rc) { cFYI(1, ("Validation prior to mmap failed, error=%d", rc)); FreeXid(xid); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 8bdbc818164..f050dba920c 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -77,6 +77,41 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) } } +/* check inode attributes against fattr. If they don't match, tag the + * inode for cache invalidation + */ +static void +cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + cFYI(1, ("%s: revalidating inode %llu", __func__, cifs_i->uniqueid)); + + if (inode->i_state & I_NEW) { + cFYI(1, ("%s: inode %llu is new", __func__, cifs_i->uniqueid)); + return; + } + + /* don't bother with revalidation if we have an oplock */ + if (cifs_i->clientCanCacheRead) { + cFYI(1, ("%s: inode %llu is oplocked", __func__, + cifs_i->uniqueid)); + return; + } + + /* revalidate if mtime or size have changed */ + if (timespec_equal(&inode->i_mtime, &fattr->cf_mtime) && + cifs_i->server_eof == fattr->cf_eof) { + cFYI(1, ("%s: inode %llu is unchanged", __func__, + cifs_i->uniqueid)); + return; + } + + cFYI(1, ("%s: invalidating inode %llu mapping", __func__, + cifs_i->uniqueid)); + cifs_i->invalid_mapping = true; +} + /* populate an inode with info from a cifs_fattr struct */ void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) @@ -85,6 +120,8 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); unsigned long oldtime = cifs_i->time; + cifs_revalidate_cache(inode, fattr); + inode->i_atime = fattr->cf_atime; inode->i_mtime = fattr->cf_mtime; inode->i_ctime = fattr->cf_ctime; @@ -1389,135 +1426,83 @@ cifs_rename_exit: return rc; } -int cifs_revalidate(struct dentry *direntry) +static bool +cifs_inode_needs_reval(struct inode *inode) { - int xid; - int rc = 0, wbrc = 0; - char *full_path; - struct cifs_sb_info *cifs_sb; - struct cifsInodeInfo *cifsInode; - loff_t local_size; - struct timespec local_mtime; - bool invalidate_inode = false; + struct cifsInodeInfo *cifs_i = CIFS_I(inode); - if (direntry->d_inode == NULL) - return -ENOENT; + if (cifs_i->clientCanCacheRead) + return false; - cifsInode = CIFS_I(direntry->d_inode); + if (!lookupCacheEnabled) + return true; - if (cifsInode == NULL) - return -ENOENT; + if (cifs_i->time == 0) + return true; - /* no sense revalidating inode info on file that no one can write */ - if (CIFS_I(direntry->d_inode)->clientCanCacheRead) - return rc; + /* FIXME: the actimeo should be tunable */ + if (time_after_eq(jiffies, cifs_i->time + HZ)) + return true; + + return false; +} + +/* check invalid_mapping flag and zap the cache if it's set */ +static void +cifs_invalidate_mapping(struct inode *inode) +{ + int rc; + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + cifs_i->invalid_mapping = false; + + /* write back any cached data */ + if (inode->i_mapping && inode->i_mapping->nrpages != 0) { + rc = filemap_write_and_wait(inode->i_mapping); + if (rc) + cifs_i->write_behind_rc = rc; + } + invalidate_remote_inode(inode); +} + +/* revalidate a dentry's inode attributes */ +int cifs_revalidate_dentry(struct dentry *dentry) +{ + int xid; + int rc = 0; + char *full_path = NULL; + struct inode *inode = dentry->d_inode; + struct super_block *sb = dentry->d_sb; + + if (inode == NULL) + return -ENOENT; xid = GetXid(); - cifs_sb = CIFS_SB(direntry->d_sb); + if (!cifs_inode_needs_reval(inode)) + goto check_inval; /* can not safely grab the rename sem here if rename calls revalidate since that would deadlock */ - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto check_inval; } - cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " - "jiffies %ld", full_path, direntry->d_inode, - direntry->d_inode->i_count.counter, direntry, - direntry->d_time, jiffies)); - - if (cifsInode->time == 0) { - /* was set to zero previously to force revalidate */ - } else if (time_before(jiffies, cifsInode->time + HZ) && - lookupCacheEnabled) { - if ((S_ISREG(direntry->d_inode->i_mode) == 0) || - (direntry->d_inode->i_nlink == 1)) { - kfree(full_path); - FreeXid(xid); - return rc; - } else { - cFYI(1, ("Have to revalidate file due to hardlinks")); - } - } - - /* save mtime and size */ - local_mtime = direntry->d_inode->i_mtime; - local_size = direntry->d_inode->i_size; - if (cifs_sb->tcon->unix_ext) { - rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path, - direntry->d_sb, xid); - if (rc) { - cFYI(1, ("error on getting revalidate info %d", rc)); -/* if (rc != -ENOENT) - rc = 0; */ /* BB should we cache info on - certain errors? */ - } - } else { - rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL, - direntry->d_sb, xid, NULL); - if (rc) { - cFYI(1, ("error on getting revalidate info %d", rc)); -/* if (rc != -ENOENT) - rc = 0; */ /* BB should we cache info on - certain errors? */ - } - } - /* should we remap certain errors, access denied?, to zero */ - - /* if not oplocked, we invalidate inode pages if mtime or file size - had changed on server */ + cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " + "jiffies %ld", full_path, inode, inode->i_count.counter, + dentry, dentry->d_time, jiffies)); - if (timespec_equal(&local_mtime, &direntry->d_inode->i_mtime) && - (local_size == direntry->d_inode->i_size)) { - cFYI(1, ("cifs_revalidate - inode unchanged")); - } else { - /* file may have changed on server */ - if (cifsInode->clientCanCacheRead) { - /* no need to invalidate inode pages since we were the - only ones who could have modified the file and the - server copy is staler than ours */ - } else { - invalidate_inode = true; - } - } + if (CIFS_SB(sb)->tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, NULL, sb, + xid, NULL); - /* can not grab this sem since kernel filesys locking documentation - indicates i_mutex may be taken by the kernel on lookup and rename - which could deadlock if we grab the i_mutex here as well */ -/* mutex_lock(&direntry->d_inode->i_mutex);*/ - /* need to write out dirty pages here */ - if (direntry->d_inode->i_mapping) { - /* do we need to lock inode until after invalidate completes - below? */ - wbrc = filemap_fdatawrite(direntry->d_inode->i_mapping); - if (wbrc) - CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; - } - if (invalidate_inode) { - /* shrink_dcache not necessary now that cifs dentry ops - are exported for negative dentries */ -/* if (S_ISDIR(direntry->d_inode->i_mode)) - shrink_dcache_parent(direntry); */ - if (S_ISREG(direntry->d_inode->i_mode)) { - if (direntry->d_inode->i_mapping) { - wbrc = filemap_fdatawait(direntry->d_inode->i_mapping); - if (wbrc) - CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; - } - /* may eventually have to do this for open files too */ - if (list_empty(&(cifsInode->openFileList))) { - /* changed on server - flush read ahead pages */ - cFYI(1, ("Invalidating read ahead data on " - "closed file")); - invalidate_remote_inode(direntry->d_inode); - } - } - } -/* mutex_unlock(&direntry->d_inode->i_mutex); */ +check_inval: + if (CIFS_I(inode)->invalid_mapping) + cifs_invalidate_mapping(inode); kfree(full_path); FreeXid(xid); @@ -1527,7 +1512,7 @@ int cifs_revalidate(struct dentry *direntry) int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - int err = cifs_revalidate(dentry); + int err = cifs_revalidate_dentry(dentry); if (!err) { generic_fillattr(dentry->d_inode, stat); stat->blksize = CIFS_MAX_MSGSIZE; -- cgit v1.2.3-18-g5258 From bcd5357f430363376565d07ca542127d6d36602c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 12 Feb 2010 07:44:16 -0500 Subject: cifs: add a CIFSSMBQFileInfo function ...to get inode attributes via filehandle instead of by path. In some places, we need to revalidate an inode on an open filehandle, but we can't necessarily guarantee that the dentry associated with it will still be valid. When we have an open filehandle already, it makes more sense to do a filehandle based operation anyway. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 88e2bc44ac5..bf2bff14c24 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -142,6 +142,8 @@ extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, extern int CIFSFindClose(const int, struct cifsTconInfo *tcon, const __u16 search_handle); +extern int CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon, + u16 netfid, FILE_ALL_INFO *pFindData); extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO *findData, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 9d17df3e076..4ed97825db3 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3230,8 +3230,72 @@ QInfRetry: return rc; } +int +CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon, + u16 netfid, FILE_ALL_INFO *pFindData) +{ + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int rc = 0; + int bytes_returned; + __u16 params, byte_count; + +QFileInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + pSMB->Pad = 0; + pSMB->Fid = netfid; + pSMB->hdr.smb_buf_length += byte_count; + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in QPathInfo = %d", rc)); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc) /* BB add auto retry on EOPNOTSUPP? */ + rc = -EIO; + else if (pSMBr->ByteCount < 40) + rc = -EIO; /* bad smb */ + else if (pFindData) { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, sizeof(FILE_ALL_INFO)); + } else + rc = -ENOMEM; + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QFileInfoRetry; + return rc; +} int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, -- cgit v1.2.3-18-g5258 From c8634fd3115497ac311f57be9c12f993437745cf Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 12 Feb 2010 07:44:17 -0500 Subject: cifs: add a CIFSSMBUnixQFileInfo function ...to allow us to get unix attrs via filehandle. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index bf2bff14c24..ce9199f0af9 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -154,6 +154,8 @@ extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, FILE_ALL_INFO *findData, const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); extern int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 4ed97825db3..903d53871da 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3398,6 +3398,75 @@ QPathInfoRetry: return rc; } +int +CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) +{ + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int rc = 0; + int bytes_returned; + __u16 params, byte_count; + +UnixQFileInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pSMB->Pad = 0; + pSMB->Fid = netfid; + pSMB->hdr.smb_buf_length += byte_count; + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in QPathInfo = %d", rc)); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) { + cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n" + "Unix Extensions can be disabled on mount " + "by specifying the nosfu mount option.")); + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, + sizeof(FILE_UNIX_BASIC_INFO)); + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto UnixQFileInfoRetry; + + return rc; +} + int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, -- cgit v1.2.3-18-g5258 From abab095d1fd25986b910d3c46289d8fa3582cdc5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 12 Feb 2010 07:44:18 -0500 Subject: cifs: add cifs_revalidate_file ...to allow updating inode attributes on an existing inode by filehandle. Change mmap and llseek codepaths to use that instead of cifs_revalidate_dentry since they have a filehandle readily available. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 2 +- fs/cifs/cifsfs.h | 1 + fs/cifs/cifsproto.h | 2 ++ fs/cifs/file.c | 3 +- fs/cifs/inode.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 3 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index cf85a4165b0..5183bc2a191 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -639,7 +639,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) setting the revalidate time to zero */ CIFS_I(file->f_path.dentry->d_inode)->time = 0; - retval = cifs_revalidate_dentry(file->f_path.dentry); + retval = cifs_revalidate_file(file); if (retval < 0) return (loff_t)retval; } diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 2af995ca895..7aa57ecdc43 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -61,6 +61,7 @@ extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +extern int cifs_revalidate_file(struct file *filp); extern int cifs_revalidate_dentry(struct dentry *); extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_setattr(struct dentry *, struct iattr *); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ce9199f0af9..39e47f46dea 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -104,10 +104,12 @@ extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr); extern struct inode *cifs_iget(struct super_block *sb, struct cifs_fattr *fattr); +extern int cifs_get_file_info(struct file *filp); extern int cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path, FILE_ALL_INFO *pfile_info, struct super_block *sb, int xid, const __u16 *pfid); +extern int cifs_get_file_info_unix(struct file *filp); extern int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, int xid); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b90f8f2ca85..1389f6ecef9 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1890,11 +1890,10 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) { - struct dentry *dentry = file->f_path.dentry; int rc, xid; xid = GetXid(); - rc = cifs_revalidate_dentry(dentry); + rc = cifs_revalidate_file(file); if (rc) { cFYI(1, ("Validation prior to mmap failed, error=%d", rc)); FreeXid(xid); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f050dba920c..0d034a84bb8 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -268,6 +268,31 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL; } +int cifs_get_file_info_unix(struct file *filp) +{ + int rc; + int xid; + FILE_UNIX_BASIC_INFO find_data; + struct cifs_fattr fattr; + struct inode *inode = filp->f_path.dentry->d_inode; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + + xid = GetXid(); + rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + } + + cifs_fattr_to_inode(inode, &fattr); + FreeXid(xid); + return rc; +} + int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *full_path, struct super_block *sb, int xid) @@ -469,6 +494,47 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, fattr->cf_gid = cifs_sb->mnt_gid; } +int cifs_get_file_info(struct file *filp) +{ + int rc; + int xid; + FILE_ALL_INFO find_data; + struct cifs_fattr fattr; + struct inode *inode = filp->f_path.dentry->d_inode; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + + xid = GetXid(); + rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); + if (rc == -EOPNOTSUPP || rc == -EINVAL) { + /* + * FIXME: legacy server -- fall back to path-based call? + * for now, just skip revalidating and mark inode for + * immediate reval. + */ + rc = 0; + CIFS_I(inode)->time = 0; + goto cgfi_exit; + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + } else if (rc) + goto cgfi_exit; + + /* + * don't bother with SFU junk here -- just mark inode as needing + * revalidation. + */ + cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false); + fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + cifs_fattr_to_inode(inode, &fattr); +cgfi_exit: + FreeXid(xid); + return rc; +} + int cifs_get_inode_info(struct inode **pinode, const unsigned char *full_path, FILE_ALL_INFO *pfindData, struct super_block *sb, int xid, const __u16 *pfid) @@ -1465,6 +1531,26 @@ cifs_invalidate_mapping(struct inode *inode) invalidate_remote_inode(inode); } +int cifs_revalidate_file(struct file *filp) +{ + int rc = 0; + struct inode *inode = filp->f_path.dentry->d_inode; + + if (!cifs_inode_needs_reval(inode)) + goto check_inval; + + if (CIFS_SB(inode->i_sb)->tcon->unix_ext) + rc = cifs_get_file_info_unix(filp); + else + rc = cifs_get_file_info(filp); + +check_inval: + if (CIFS_I(inode)->invalid_mapping) + cifs_invalidate_mapping(inode); + + return rc; +} + /* revalidate a dentry's inode attributes */ int cifs_revalidate_dentry(struct dentry *dentry) { -- cgit v1.2.3-18-g5258 From ff215713eb33c56301cf6bfec0143ddc7f22c138 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 9 Mar 2010 20:30:42 +0000 Subject: [CIFS] checkpatch cleanup Signed-off-by: Steve French --- fs/cifs/file.c | 4 ++-- fs/cifs/inode.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1389f6ecef9..ca2ba7a0193 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -219,8 +219,8 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, cFYI(1, ("inode unchanged on server")); } else { if (file->f_path.dentry->d_inode->i_mapping) { - /* BB no need to lock inode until after invalidate - since namei code should already have it locked? */ + /* BB no need to lock inode until after invalidate + since namei code should already have it locked? */ rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping); if (rc != 0) CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 0d034a84bb8..723daaccbd0 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -510,9 +510,9 @@ int cifs_get_file_info(struct file *filp) if (rc == -EOPNOTSUPP || rc == -EINVAL) { /* * FIXME: legacy server -- fall back to path-based call? - * for now, just skip revalidating and mark inode for - * immediate reval. - */ + * for now, just skip revalidating and mark inode for + * immediate reval. + */ rc = 0; CIFS_I(inode)->time = 0; goto cgfi_exit; -- cgit v1.2.3-18-g5258 From 8212cf7583a5ba5d213d9c9180be808222a2813f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 15 Mar 2010 11:22:26 +0300 Subject: cifs: trivial white space I fixed the indent level. Signed-off-by: Dan Carpenter Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 903d53871da..20959befc70 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -500,7 +500,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) } else if (pSMBr->hdr.WordCount == 13) { cERROR(1, ("mount failed, cifs module not built " "with CIFS_WEAK_PW_HASH support")); - rc = -EOPNOTSUPP; + rc = -EOPNOTSUPP; #endif /* WEAK_PW_HASH */ goto neg_err_exit; } else if (pSMBr->hdr.WordCount != 17) { -- cgit v1.2.3-18-g5258 From 810627a013163cd294762d57c0ea2ec055ffe4f6 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Sat, 27 Mar 2010 02:00:49 +0000 Subject: [CIFS] Add mmap for direct, nobrl cifs mount types without mmap functions in file_ops OpenOffice can't save changes in existing document. The same situation you can see with gedit. Also, a.out format of files can't be executed without mmap. Signed-off-by: Pavel Shilovsky Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 5183bc2a191..ded66be6597 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -808,6 +808,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { .release = cifs_close, .fsync = cifs_fsync, .flush = cifs_flush, + .mmap = cifs_file_mmap, .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .unlocked_ioctl = cifs_ioctl, -- cgit v1.2.3-18-g5258 From 5a0e3ad6af8660be21ca98a971cd00f331318c05 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 24 Mar 2010 17:04:11 +0900 Subject: include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo Guess-its-ok-by: Christoph Lameter Cc: Ingo Molnar Cc: Lee Schermerhorn --- fs/cifs/cifs_dfs_ref.c | 1 + fs/cifs/cifs_spnego.c | 1 + fs/cifs/cifs_unicode.c | 1 + fs/cifs/cifsacl.c | 1 + fs/cifs/cifsencrypt.c | 1 + fs/cifs/cifsglob.h | 1 + fs/cifs/cifssmb.c | 1 + fs/cifs/connect.c | 1 + fs/cifs/dns_resolve.c | 1 + fs/cifs/file.c | 1 + fs/cifs/inode.c | 1 + fs/cifs/link.c | 1 + fs/cifs/readdir.c | 1 + fs/cifs/sess.c | 1 + fs/cifs/smbencrypt.c | 1 + fs/cifs/transport.c | 1 + fs/cifs/xattr.c | 1 + 17 files changed, 17 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index b1d61d0bdfc..78e4d2a3a68 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include "cifsglob.h" diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 8ec7736ce95..310d12f69a9 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 714a542cbaf..d07676bd76d 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include "cifs_unicode.h" #include "cifs_uniupr.h" #include "cifspdu.h" diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 7dfe0842a6f..9b716d044bb 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -22,6 +22,7 @@ */ #include +#include #include "cifspdu.h" #include "cifsglob.h" #include "cifsacl.h" diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 7efe1745494..fbe986430d0 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -20,6 +20,7 @@ */ #include +#include #include "cifspdu.h" #include "cifsglob.h" #include "cifs_debug.h" diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 63c89d1d70b..ecf0ffbe2b6 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -18,6 +18,7 @@ */ #include #include +#include #include #include "cifs_fs_sb.h" #include "cifsacl.h" diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 7cc7f83e931..3f4fbd67050 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "cifspdu.h" diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 45eb6cba793..d9566bf8f91 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 87948147d7e..6f8a0e3fb25 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -23,6 +23,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include "dns_resolve.h" #include "cifsglob.h" diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ca2ba7a0193..058b390d3da 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 723daaccbd0..35ec1171621 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include #include "cifsfs.h" diff --git a/fs/cifs/link.c b/fs/cifs/link.c index fc1e0487eae..c1a9d4236a8 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index c343b14ba2d..18e0bc1fb59 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -22,6 +22,7 @@ */ #include #include +#include #include #include "cifspdu.h" #include "cifsglob.h" diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index aaa9c1c5a5b..7c3fd7463f4 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -29,6 +29,7 @@ #include "ntlmssp.h" #include "nterr.h" #include +#include #include "cifs_spnego.h" extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 93fb09a99c6..192ea51af20 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -24,6 +24,7 @@ */ #include +#include #include #include #include diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 07b8e71544e..ad081fe7eb1 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 3e2ef0de120..f555ce077d4 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -21,6 +21,7 @@ #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" -- cgit v1.2.3-18-g5258 From a24e2d7d8f512340991ef0a59cb5d08d491b8e98 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 3 Apr 2010 17:20:21 +0000 Subject: [CIFS] initialize nbytes at the beginning of CIFSSMBWrite() By doing this we always overwrite nbytes value that is being passed on to CIFSSMBWrite() and need not rely on the callers to initialize. CIFSSMBWrite2 is doing this already. CC: Stable Reviewed-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 7cc7f83e931..e1f90a3a016 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1430,6 +1430,8 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, __u32 bytes_sent; __u16 byte_count; + *nbytes = 0; + /* cFYI(1, ("write at %lld %d bytes", offset, count));*/ if (tcon->ses == NULL) return -ECONNABORTED; @@ -1512,7 +1514,6 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error in write = %d", rc)); - *nbytes = 0; } else { *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; -- cgit v1.2.3-18-g5258 From 6513a81e9325d712f1bfb9a1d7b750134e49ff18 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Wed, 31 Mar 2010 12:00:03 +0530 Subject: cifs: Fix a kernel BUG with remote OS/2 server (try #3) While chasing a bug report involving a OS/2 server, I noticed the server sets pSMBr->CountHigh to a incorrect value even in case of normal writes. This results in 'nbytes' being computed wrongly and triggers a kernel BUG at mm/filemap.c. void iov_iter_advance(struct iov_iter *i, size_t bytes) { BUG_ON(i->count < bytes); <--- BUG here Why the server is setting 'CountHigh' is not clear but only does so after writing 64k bytes. Though this looks like the server bug, the client side crash may not be acceptable. The workaround is to mask off high 16 bits if the number of bytes written as returned by the server is greater than the bytes requested by the client as suggested by Jeff Layton. CC: Stable Reviewed-by: Jeff Layton Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index e1f90a3a016..f213b8ae43c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1518,6 +1518,14 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; *nbytes += le16_to_cpu(pSMBr->Count); + + /* + * Mask off high 16 bits when bytes written as returned by the + * server is greater than bytes requested by the client. Some + * OS/2 servers are known to set incorrect CountHigh values. + */ + if (*nbytes > count) + *nbytes &= 0xFFFF; } cifs_buf_release(pSMB); @@ -1606,6 +1614,14 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; *nbytes += le16_to_cpu(pSMBr->Count); + + /* + * Mask off high 16 bits when bytes written as returned by the + * server is greater than bytes requested by the client. OS/2 + * servers are known to set incorrect CountHigh values. + */ + if (*nbytes > count) + *nbytes &= 0xFFFF; } /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ -- cgit v1.2.3-18-g5258 From f05337c6ac48d19d354e0640a8eb8fc884f82bcc Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 5 Apr 2010 09:59:14 +0400 Subject: not overwriting file_lock structure after GET_LK If we have preventing lock, cifs should overwrite file_lock structure with info about preventing lock. If we haven't preventing lock, cifs should leave it unchanged except for the lock type (change it to F_UNLCK). Signed-off-by: Pavel Shilovsky Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 15 ++++++++++++++- fs/cifs/file.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f213b8ae43c..184a399749a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1810,8 +1810,21 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, } parm_data = (struct cifs_posix_lock *) ((char *)&pSMBr->hdr.Protocol + data_offset); - if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) + if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK)) pLockData->fl_type = F_UNLCK; + else { + if (parm_data->lock_type == + __constant_cpu_to_le16(CIFS_RDLCK)) + pLockData->fl_type = F_RDLCK; + else if (parm_data->lock_type == + __constant_cpu_to_le16(CIFS_WRLCK)) + pLockData->fl_type = F_WRLCK; + + pLockData->fl_start = parm_data->start; + pLockData->fl_end = parm_data->start + + parm_data->length - 1; + pLockData->fl_pid = parm_data->pid; + } } plk_err_exit: diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ca2ba7a0193..d9e86504b9d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -838,8 +838,32 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) } else { /* if rc == ERR_SHARING_VIOLATION ? */ - rc = 0; /* do not change lock type to unlock - since range in use */ + rc = 0; + + if (lockType & LOCKING_ANDX_SHARED_LOCK) { + pfLock->fl_type = F_WRLCK; + } else { + rc = CIFSSMBLock(xid, tcon, netfid, length, + pfLock->fl_start, 0, 1, + lockType | LOCKING_ANDX_SHARED_LOCK, + 0 /* wait flag */); + if (rc == 0) { + rc = CIFSSMBLock(xid, tcon, netfid, + length, pfLock->fl_start, 1, 0, + lockType | + LOCKING_ANDX_SHARED_LOCK, + 0 /* wait flag */); + pfLock->fl_type = F_RDLCK; + if (rc != 0) + cERROR(1, ("Error unlocking " + "previously locked range %d " + "during test of lock", rc)); + rc = 0; + } else { + pfLock->fl_type = F_WRLCK; + rc = 0; + } + } } FreeXid(xid); -- cgit v1.2.3-18-g5258