]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - queue-6.1/cifs-fix-caching-to-try-to-do-open-o_wronly-as-rdwr-.patch
6.1-stable patches
[thirdparty/kernel/stable-queue.git] / queue-6.1 / cifs-fix-caching-to-try-to-do-open-o_wronly-as-rdwr-.patch
1 From 71f33f7378dd4656783b83ab086106f7453d104d Mon Sep 17 00:00:00 2001
2 From: Sasha Levin <sashal@kernel.org>
3 Date: Tue, 2 Apr 2024 10:11:35 +0100
4 Subject: cifs: Fix caching to try to do open O_WRONLY as rdwr on server
5
6 From: David Howells <dhowells@redhat.com>
7
8 [ Upstream commit e9e62243a3e2322cf639f653a0b0a88a76446ce7 ]
9
10 When we're engaged in local caching of a cifs filesystem, we cannot perform
11 caching of a partially written cache granule unless we can read the rest of
12 the granule. This can result in unexpected access errors being reported to
13 the user.
14
15 Fix this by the following: if a file is opened O_WRONLY locally, but the
16 mount was given the "-o fsc" flag, try first opening the remote file with
17 GENERIC_READ|GENERIC_WRITE and if that returns -EACCES, try dropping the
18 GENERIC_READ and doing the open again. If that last succeeds, invalidate
19 the cache for that file as for O_DIRECT.
20
21 Fixes: 70431bfd825d ("cifs: Support fscache indexing rewrite")
22 Signed-off-by: David Howells <dhowells@redhat.com>
23 cc: Steve French <sfrench@samba.org>
24 cc: Shyam Prasad N <nspmangalore@gmail.com>
25 cc: Rohith Surabattula <rohiths.msft@gmail.com>
26 cc: Jeff Layton <jlayton@kernel.org>
27 cc: linux-cifs@vger.kernel.org
28 cc: netfs@lists.linux.dev
29 cc: linux-fsdevel@vger.kernel.org
30 Signed-off-by: Steve French <stfrench@microsoft.com>
31 Signed-off-by: Sasha Levin <sashal@kernel.org>
32 ---
33 fs/smb/client/dir.c | 15 +++++++++++++
34 fs/smb/client/file.c | 48 ++++++++++++++++++++++++++++++++---------
35 fs/smb/client/fscache.h | 6 ++++++
36 3 files changed, 59 insertions(+), 10 deletions(-)
37
38 diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
39 index e382b794acbed..863c7bc3db86f 100644
40 --- a/fs/smb/client/dir.c
41 +++ b/fs/smb/client/dir.c
42 @@ -180,6 +180,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
43 int disposition;
44 struct TCP_Server_Info *server = tcon->ses->server;
45 struct cifs_open_parms oparms;
46 + int rdwr_for_fscache = 0;
47
48 *oplock = 0;
49 if (tcon->ses->server->oplocks)
50 @@ -191,6 +192,10 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
51 return PTR_ERR(full_path);
52 }
53
54 + /* If we're caching, we need to be able to fill in around partial writes. */
55 + if (cifs_fscache_enabled(inode) && (oflags & O_ACCMODE) == O_WRONLY)
56 + rdwr_for_fscache = 1;
57 +
58 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
59 if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
60 (CIFS_UNIX_POSIX_PATH_OPS_CAP &
61 @@ -267,6 +272,8 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
62 desired_access |= GENERIC_READ; /* is this too little? */
63 if (OPEN_FMODE(oflags) & FMODE_WRITE)
64 desired_access |= GENERIC_WRITE;
65 + if (rdwr_for_fscache == 1)
66 + desired_access |= GENERIC_READ;
67
68 disposition = FILE_OVERWRITE_IF;
69 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
70 @@ -295,6 +302,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
71 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
72 create_options |= CREATE_OPTION_READONLY;
73
74 +retry_open:
75 oparms = (struct cifs_open_parms) {
76 .tcon = tcon,
77 .cifs_sb = cifs_sb,
78 @@ -308,8 +316,15 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
79 rc = server->ops->open(xid, &oparms, oplock, buf);
80 if (rc) {
81 cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
82 + if (rc == -EACCES && rdwr_for_fscache == 1) {
83 + desired_access &= ~GENERIC_READ;
84 + rdwr_for_fscache = 2;
85 + goto retry_open;
86 + }
87 goto out;
88 }
89 + if (rdwr_for_fscache == 2)
90 + cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
91
92 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
93 /*
94 diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
95 index 0f3405e0f2e48..c240cea7ca349 100644
96 --- a/fs/smb/client/file.c
97 +++ b/fs/smb/client/file.c
98 @@ -77,12 +77,12 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
99 */
100 }
101
102 -static inline int cifs_convert_flags(unsigned int flags)
103 +static inline int cifs_convert_flags(unsigned int flags, int rdwr_for_fscache)
104 {
105 if ((flags & O_ACCMODE) == O_RDONLY)
106 return GENERIC_READ;
107 else if ((flags & O_ACCMODE) == O_WRONLY)
108 - return GENERIC_WRITE;
109 + return rdwr_for_fscache == 1 ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
110 else if ((flags & O_ACCMODE) == O_RDWR) {
111 /* GENERIC_ALL is too much permission to request
112 can cause unnecessary access denied on create */
113 @@ -219,11 +219,16 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
114 int create_options = CREATE_NOT_DIR;
115 struct TCP_Server_Info *server = tcon->ses->server;
116 struct cifs_open_parms oparms;
117 + int rdwr_for_fscache = 0;
118
119 if (!server->ops->open)
120 return -ENOSYS;
121
122 - desired_access = cifs_convert_flags(f_flags);
123 + /* If we're caching, we need to be able to fill in around partial writes. */
124 + if (cifs_fscache_enabled(inode) && (f_flags & O_ACCMODE) == O_WRONLY)
125 + rdwr_for_fscache = 1;
126 +
127 + desired_access = cifs_convert_flags(f_flags, rdwr_for_fscache);
128
129 /*********************************************************************
130 * open flag mapping table:
131 @@ -260,6 +265,7 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
132 if (f_flags & O_DIRECT)
133 create_options |= CREATE_NO_BUFFER;
134
135 +retry_open:
136 oparms = (struct cifs_open_parms) {
137 .tcon = tcon,
138 .cifs_sb = cifs_sb,
139 @@ -271,8 +277,16 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
140 };
141
142 rc = server->ops->open(xid, &oparms, oplock, buf);
143 - if (rc)
144 + if (rc) {
145 + if (rc == -EACCES && rdwr_for_fscache == 1) {
146 + desired_access = cifs_convert_flags(f_flags, 0);
147 + rdwr_for_fscache = 2;
148 + goto retry_open;
149 + }
150 return rc;
151 + }
152 + if (rdwr_for_fscache == 2)
153 + cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
154
155 /* TODO: Add support for calling posix query info but with passing in fid */
156 if (tcon->unix_ext)
157 @@ -705,11 +719,11 @@ int cifs_open(struct inode *inode, struct file *file)
158 use_cache:
159 fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
160 file->f_mode & FMODE_WRITE);
161 - if (file->f_flags & O_DIRECT &&
162 - (!((file->f_flags & O_ACCMODE) != O_RDONLY) ||
163 - file->f_flags & O_APPEND))
164 - cifs_invalidate_cache(file_inode(file),
165 - FSCACHE_INVAL_DIO_WRITE);
166 + if (!(file->f_flags & O_DIRECT))
167 + goto out;
168 + if ((file->f_flags & (O_ACCMODE | O_APPEND)) == O_RDONLY)
169 + goto out;
170 + cifs_invalidate_cache(file_inode(file), FSCACHE_INVAL_DIO_WRITE);
171
172 out:
173 free_dentry_path(page);
174 @@ -774,6 +788,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
175 int disposition = FILE_OPEN;
176 int create_options = CREATE_NOT_DIR;
177 struct cifs_open_parms oparms;
178 + int rdwr_for_fscache = 0;
179
180 xid = get_xid();
181 mutex_lock(&cfile->fh_mutex);
182 @@ -837,7 +852,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
183 }
184 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
185
186 - desired_access = cifs_convert_flags(cfile->f_flags);
187 + /* If we're caching, we need to be able to fill in around partial writes. */
188 + if (cifs_fscache_enabled(inode) && (cfile->f_flags & O_ACCMODE) == O_WRONLY)
189 + rdwr_for_fscache = 1;
190 +
191 + desired_access = cifs_convert_flags(cfile->f_flags, rdwr_for_fscache);
192
193 /* O_SYNC also has bit for O_DSYNC so following check picks up either */
194 if (cfile->f_flags & O_SYNC)
195 @@ -849,6 +868,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
196 if (server->ops->get_lease_key)
197 server->ops->get_lease_key(inode, &cfile->fid);
198
199 +retry_open:
200 oparms = (struct cifs_open_parms) {
201 .tcon = tcon,
202 .cifs_sb = cifs_sb,
203 @@ -874,6 +894,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
204 /* indicate that we need to relock the file */
205 oparms.reconnect = true;
206 }
207 + if (rc == -EACCES && rdwr_for_fscache == 1) {
208 + desired_access = cifs_convert_flags(cfile->f_flags, 0);
209 + rdwr_for_fscache = 2;
210 + goto retry_open;
211 + }
212
213 if (rc) {
214 mutex_unlock(&cfile->fh_mutex);
215 @@ -882,6 +907,9 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
216 goto reopen_error_exit;
217 }
218
219 + if (rdwr_for_fscache == 2)
220 + cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
221 +
222 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
223 reopen_success:
224 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
225 diff --git a/fs/smb/client/fscache.h b/fs/smb/client/fscache.h
226 index 67b601041f0a3..c691b98b442a6 100644
227 --- a/fs/smb/client/fscache.h
228 +++ b/fs/smb/client/fscache.h
229 @@ -108,6 +108,11 @@ static inline void cifs_readpage_to_fscache(struct inode *inode,
230 __cifs_readpage_to_fscache(inode, page);
231 }
232
233 +static inline bool cifs_fscache_enabled(struct inode *inode)
234 +{
235 + return fscache_cookie_enabled(cifs_inode_cookie(inode));
236 +}
237 +
238 #else /* CONFIG_CIFS_FSCACHE */
239 static inline
240 void cifs_fscache_fill_coherency(struct inode *inode,
241 @@ -123,6 +128,7 @@ static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
242 static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {}
243 static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; }
244 static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {}
245 +static inline bool cifs_fscache_enabled(struct inode *inode) { return false; }
246
247 static inline int cifs_fscache_query_occupancy(struct inode *inode,
248 pgoff_t first, unsigned int nr_pages,
249 --
250 2.43.0
251