Skip to content

Commit e9e6224

Browse files
dhowellsSteve French
authored andcommitted
cifs: Fix caching to try to do open O_WRONLY as rdwr on server
When we're engaged in local caching of a cifs filesystem, we cannot perform caching of a partially written cache granule unless we can read the rest of the granule. This can result in unexpected access errors being reported to the user. Fix this by the following: if a file is opened O_WRONLY locally, but the mount was given the "-o fsc" flag, try first opening the remote file with GENERIC_READ|GENERIC_WRITE and if that returns -EACCES, try dropping the GENERIC_READ and doing the open again. If that last succeeds, invalidate the cache for that file as for O_DIRECT. Fixes: 70431bf ("cifs: Support fscache indexing rewrite") Signed-off-by: David Howells <[email protected]> cc: Steve French <[email protected]> cc: Shyam Prasad N <[email protected]> cc: Rohith Surabattula <[email protected]> cc: Jeff Layton <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] Signed-off-by: Steve French <[email protected]>
1 parent 24a9799 commit e9e6224

File tree

3 files changed

+59
-10
lines changed

3 files changed

+59
-10
lines changed

fs/smb/client/dir.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
189189
int disposition;
190190
struct TCP_Server_Info *server = tcon->ses->server;
191191
struct cifs_open_parms oparms;
192+
int rdwr_for_fscache = 0;
192193

193194
*oplock = 0;
194195
if (tcon->ses->server->oplocks)
@@ -200,6 +201,10 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
200201
return PTR_ERR(full_path);
201202
}
202203

204+
/* If we're caching, we need to be able to fill in around partial writes. */
205+
if (cifs_fscache_enabled(inode) && (oflags & O_ACCMODE) == O_WRONLY)
206+
rdwr_for_fscache = 1;
207+
203208
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
204209
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
205210
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
@@ -276,6 +281,8 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
276281
desired_access |= GENERIC_READ; /* is this too little? */
277282
if (OPEN_FMODE(oflags) & FMODE_WRITE)
278283
desired_access |= GENERIC_WRITE;
284+
if (rdwr_for_fscache == 1)
285+
desired_access |= GENERIC_READ;
279286

280287
disposition = FILE_OVERWRITE_IF;
281288
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
@@ -304,6 +311,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
304311
if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
305312
create_options |= CREATE_OPTION_READONLY;
306313

314+
retry_open:
307315
oparms = (struct cifs_open_parms) {
308316
.tcon = tcon,
309317
.cifs_sb = cifs_sb,
@@ -317,8 +325,15 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
317325
rc = server->ops->open(xid, &oparms, oplock, buf);
318326
if (rc) {
319327
cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
328+
if (rc == -EACCES && rdwr_for_fscache == 1) {
329+
desired_access &= ~GENERIC_READ;
330+
rdwr_for_fscache = 2;
331+
goto retry_open;
332+
}
320333
goto out;
321334
}
335+
if (rdwr_for_fscache == 2)
336+
cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
322337

323338
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
324339
/*

fs/smb/client/file.c

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,12 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
206206
*/
207207
}
208208

209-
static inline int cifs_convert_flags(unsigned int flags)
209+
static inline int cifs_convert_flags(unsigned int flags, int rdwr_for_fscache)
210210
{
211211
if ((flags & O_ACCMODE) == O_RDONLY)
212212
return GENERIC_READ;
213213
else if ((flags & O_ACCMODE) == O_WRONLY)
214-
return GENERIC_WRITE;
214+
return rdwr_for_fscache == 1 ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
215215
else if ((flags & O_ACCMODE) == O_RDWR) {
216216
/* GENERIC_ALL is too much permission to request
217217
can cause unnecessary access denied on create */
@@ -348,11 +348,16 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
348348
int create_options = CREATE_NOT_DIR;
349349
struct TCP_Server_Info *server = tcon->ses->server;
350350
struct cifs_open_parms oparms;
351+
int rdwr_for_fscache = 0;
351352

352353
if (!server->ops->open)
353354
return -ENOSYS;
354355

355-
desired_access = cifs_convert_flags(f_flags);
356+
/* If we're caching, we need to be able to fill in around partial writes. */
357+
if (cifs_fscache_enabled(inode) && (f_flags & O_ACCMODE) == O_WRONLY)
358+
rdwr_for_fscache = 1;
359+
360+
desired_access = cifs_convert_flags(f_flags, rdwr_for_fscache);
356361

357362
/*********************************************************************
358363
* open flag mapping table:
@@ -389,6 +394,7 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
389394
if (f_flags & O_DIRECT)
390395
create_options |= CREATE_NO_BUFFER;
391396

397+
retry_open:
392398
oparms = (struct cifs_open_parms) {
393399
.tcon = tcon,
394400
.cifs_sb = cifs_sb,
@@ -400,8 +406,16 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
400406
};
401407

402408
rc = server->ops->open(xid, &oparms, oplock, buf);
403-
if (rc)
409+
if (rc) {
410+
if (rc == -EACCES && rdwr_for_fscache == 1) {
411+
desired_access = cifs_convert_flags(f_flags, 0);
412+
rdwr_for_fscache = 2;
413+
goto retry_open;
414+
}
404415
return rc;
416+
}
417+
if (rdwr_for_fscache == 2)
418+
cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
405419

406420
/* TODO: Add support for calling posix query info but with passing in fid */
407421
if (tcon->unix_ext)
@@ -834,11 +848,11 @@ int cifs_open(struct inode *inode, struct file *file)
834848
use_cache:
835849
fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
836850
file->f_mode & FMODE_WRITE);
837-
if (file->f_flags & O_DIRECT &&
838-
(!((file->f_flags & O_ACCMODE) != O_RDONLY) ||
839-
file->f_flags & O_APPEND))
840-
cifs_invalidate_cache(file_inode(file),
841-
FSCACHE_INVAL_DIO_WRITE);
851+
if (!(file->f_flags & O_DIRECT))
852+
goto out;
853+
if ((file->f_flags & (O_ACCMODE | O_APPEND)) == O_RDONLY)
854+
goto out;
855+
cifs_invalidate_cache(file_inode(file), FSCACHE_INVAL_DIO_WRITE);
842856

843857
out:
844858
free_dentry_path(page);
@@ -903,6 +917,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
903917
int disposition = FILE_OPEN;
904918
int create_options = CREATE_NOT_DIR;
905919
struct cifs_open_parms oparms;
920+
int rdwr_for_fscache = 0;
906921

907922
xid = get_xid();
908923
mutex_lock(&cfile->fh_mutex);
@@ -966,7 +981,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
966981
}
967982
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
968983

969-
desired_access = cifs_convert_flags(cfile->f_flags);
984+
/* If we're caching, we need to be able to fill in around partial writes. */
985+
if (cifs_fscache_enabled(inode) && (cfile->f_flags & O_ACCMODE) == O_WRONLY)
986+
rdwr_for_fscache = 1;
987+
988+
desired_access = cifs_convert_flags(cfile->f_flags, rdwr_for_fscache);
970989

971990
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
972991
if (cfile->f_flags & O_SYNC)
@@ -978,6 +997,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
978997
if (server->ops->get_lease_key)
979998
server->ops->get_lease_key(inode, &cfile->fid);
980999

1000+
retry_open:
9811001
oparms = (struct cifs_open_parms) {
9821002
.tcon = tcon,
9831003
.cifs_sb = cifs_sb,
@@ -1003,6 +1023,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
10031023
/* indicate that we need to relock the file */
10041024
oparms.reconnect = true;
10051025
}
1026+
if (rc == -EACCES && rdwr_for_fscache == 1) {
1027+
desired_access = cifs_convert_flags(cfile->f_flags, 0);
1028+
rdwr_for_fscache = 2;
1029+
goto retry_open;
1030+
}
10061031

10071032
if (rc) {
10081033
mutex_unlock(&cfile->fh_mutex);
@@ -1011,6 +1036,9 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
10111036
goto reopen_error_exit;
10121037
}
10131038

1039+
if (rdwr_for_fscache == 2)
1040+
cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
1041+
10141042
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
10151043
reopen_success:
10161044
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */

fs/smb/client/fscache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ static inline void cifs_readahead_to_fscache(struct inode *inode,
109109
__cifs_readahead_to_fscache(inode, pos, len);
110110
}
111111

112+
static inline bool cifs_fscache_enabled(struct inode *inode)
113+
{
114+
return fscache_cookie_enabled(cifs_inode_cookie(inode));
115+
}
116+
112117
#else /* CONFIG_CIFS_FSCACHE */
113118
static inline
114119
void cifs_fscache_fill_coherency(struct inode *inode,
@@ -124,6 +129,7 @@ static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
124129
static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {}
125130
static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; }
126131
static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {}
132+
static inline bool cifs_fscache_enabled(struct inode *inode) { return false; }
127133

128134
static inline int cifs_fscache_query_occupancy(struct inode *inode,
129135
pgoff_t first, unsigned int nr_pages,

0 commit comments

Comments
 (0)