Skip to content

Commit

Permalink
smb3: query attributes on file close
Browse files Browse the repository at this point in the history
Since timestamps on files on most servers can be updated at
close, and since timestamps on our dentries default to one
second we can have stale timestamps in some common cases
(e.g. open, write, close, stat, wait one second, stat - will
show different mtime for the first and second stat).

The SMB2/SMB3 protocol allows querying timestamps at close
so add the code to request timestamp and attr information
(which is cheap for the server to provide) to be returned
when a file is closed (it is not needed for the many
paths that call SMB2_close that are from compounded
query infos and close nor is it needed for some of
the cases where a directory close immediately follows a
directory open.

Signed-off-by: Steve French <stfrench@microsoft.com>
Acked-by: Ronnie Sahlberg <lsahlber@redhat.com>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
  • Loading branch information
Steve French committed Dec 3, 2019
1 parent 9e8fae2 commit 43f8a6a
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 15 deletions.
3 changes: 3 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@ struct smb_version_operations {
/* close a file */
void (*close)(const unsigned int, struct cifs_tcon *,
struct cifs_fid *);
/* close a file, returning file attributes and timestamps */
void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *pfile_info);
/* send a flush request to the server */
int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
/* async read from the server */
Expand Down
4 changes: 3 additions & 1 deletion fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,9 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
unsigned int xid;

xid = get_xid();
if (server->ops->close)
if (server->ops->close_getattr)
server->ops->close_getattr(xid, tcon, cifs_file);
else if (server->ops->close)
server->ops->close(xid, tcon, &cifs_file->fid);
_free_xid(xid);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/smb2inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
rqst[num_rqst].rq_iov = close_iov;
rqst[num_rqst].rq_nvec = 1;
rc = SMB2_close_init(tcon, &rqst[num_rqst], COMPOUND_FID,
COMPOUND_FID);
COMPOUND_FID, false);
smb2_set_related(&rqst[num_rqst]);
if (rc)
goto finished;
Expand Down
49 changes: 45 additions & 4 deletions fs/cifs/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
memset(&close_iov, 0, sizeof(close_iov));
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
smb2_set_related(&rqst[2]);

rc = compound_send_recv(xid, ses, flags, 3, rqst,
Expand Down Expand Up @@ -1332,6 +1332,45 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
}

static void
smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile)
{
struct smb2_file_network_open_info file_inf;
struct inode *inode;
int rc;

rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, &file_inf);
if (rc)
return;

inode = d_inode(cfile->dentry);

spin_lock(&inode->i_lock);
CIFS_I(inode)->time = jiffies;

/* Creation time should not need to be updated on close */
if (file_inf.LastWriteTime)
inode->i_mtime = cifs_NTtimeToUnix(file_inf.LastWriteTime);
if (file_inf.ChangeTime)
inode->i_ctime = cifs_NTtimeToUnix(file_inf.ChangeTime);
if (file_inf.LastAccessTime)
inode->i_atime = cifs_NTtimeToUnix(file_inf.LastAccessTime);

/*
* i_blocks is not related to (i_size / i_blksize),
* but instead 512 byte (2**9) size is required for
* calculating num blocks.
*/
if (le64_to_cpu(file_inf.AllocationSize) > 4096)
inode->i_blocks =
(512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9;

/* End of file and Attributes should not have to be updated on close */
spin_unlock(&inode->i_lock);
}

static int
SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
Expand Down Expand Up @@ -1512,7 +1551,7 @@ smb2_ioctl_query_info(const unsigned int xid,
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1;

rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
if (rc)
goto iqinf_exit;
smb2_set_related(&rqst[2]);
Expand Down Expand Up @@ -2241,7 +2280,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1;

rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
if (rc)
goto qic_exit;
smb2_set_related(&rqst[2]);
Expand Down Expand Up @@ -2654,7 +2693,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1;

rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
if (rc)
goto querty_exit;

Expand Down Expand Up @@ -4707,6 +4746,7 @@ struct smb_version_operations smb30_operations = {
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
.close_getattr = smb2_close_getattr,
.flush = smb2_flush_file,
.async_readv = smb2_async_readv,
.async_writev = smb2_async_writev,
Expand Down Expand Up @@ -4816,6 +4856,7 @@ struct smb_version_operations smb311_operations = {
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
.close_getattr = smb2_close_getattr,
.flush = smb2_flush_file,
.async_readv = smb2_async_readv,
.async_writev = smb2_async_writev,
Expand Down
38 changes: 30 additions & 8 deletions fs/cifs/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2932,7 +2932,7 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,

int
SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 persistent_fid, u64 volatile_fid)
u64 persistent_fid, u64 volatile_fid, bool query_attrs)
{
struct smb2_close_req *req;
struct kvec *iov = rqst->rq_iov;
Expand All @@ -2945,6 +2945,10 @@ SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,

req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
if (query_attrs)
req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB;
else
req->Flags = 0;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;

Expand All @@ -2959,8 +2963,9 @@ SMB2_close_free(struct smb_rqst *rqst)
}

int
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid)
__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
struct smb2_file_network_open_info *pbuf)
{
struct smb_rqst rqst;
struct smb2_close_rsp *rsp = NULL;
Expand All @@ -2970,6 +2975,7 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
int resp_buftype = CIFS_NO_BUFFER;
int rc = 0;
int flags = 0;
bool query_attrs = false;

cifs_dbg(FYI, "Close\n");

Expand All @@ -2984,8 +2990,13 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
rqst.rq_iov = iov;
rqst.rq_nvec = 1;

/* check if need to ask server to return timestamps in close response */
if (pbuf)
query_attrs = true;

trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid);
rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid);
rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid,
query_attrs);
if (rc)
goto close_exit;

Expand All @@ -2997,14 +3008,18 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid,
rc);
goto close_exit;
} else
} else {
trace_smb3_close_done(xid, persistent_fid, tcon->tid,
ses->Suid);
/*
* Note that have to subtract 4 since struct network_open_info
* has a final 4 byte pad that close response does not have
*/
if (pbuf)
memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4);
}

atomic_dec(&tcon->num_remote_opens);

/* BB FIXME - decode close response, update inode for caching */

close_exit:
SMB2_close_free(&rqst);
free_rsp_buf(resp_buftype, rsp);
Expand All @@ -3022,6 +3037,13 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}

int
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid)
{
return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL);
}

int
smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
struct kvec *iov, unsigned int min_buf_size)
Expand Down
11 changes: 11 additions & 0 deletions fs/cifs/smb2pdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -1570,6 +1570,17 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
__le64 EndOfFile; /* new end of file value */
} __packed; /* level 20 Set */

struct smb2_file_network_open_info {
__le64 CreationTime;
__le64 LastAccessTime;
__le64 LastWriteTime;
__le64 ChangeTime;
__le64 AllocationSize;
__le64 EndOfFile;
__le32 Attributes;
__le32 Reserved;
} __packed; /* level 34 Query also similar returned in close rsp and open rsp */

extern char smb2_padding[7];

#endif /* _SMB2PDU_H */
5 changes: 4 additions & 1 deletion fs/cifs/smb2proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,13 @@ extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, bool watch_tree,
u32 completion_filter);

extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
struct smb2_file_network_open_info *pbuf);
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id);
extern int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 persistent_file_id, u64 volatile_file_id);
u64 persistent_fid, u64 volatile_fid, bool query_attrs);
extern void SMB2_close_free(struct smb_rqst *rqst);
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id);
Expand Down

0 comments on commit 43f8a6a

Please sign in to comment.