Skip to content

Commit 332019e

Browse files
committed
Merge tag '5.20-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
Pull more cifs updates from Steve French: - two fixes for stable, one for a lock length miscalculation, and another fixes a lease break timeout bug - improvement to handle leases, allows the close timeout to be configured more safely - five restructuring/cleanup patches * tag '5.20-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6: cifs: Do not access tcon->cfids->cfid directly from is_path_accessible cifs: Add constructor/destructors for tcon->cfid SMB3: fix lease break timeout when multiple deferred close handles for the same file. smb3: allow deferred close timeout to be configurable cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir cifs: Move cached-dir functions into a separate file cifs: Remove {cifs,nfs}_fscache_release_page() cifs: fix lock length calculation
2 parents 8549a26 + 7eb59a9 commit 332019e

19 files changed

+528
-445
lines changed

fs/cifs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
77

88
cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
99
inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
10-
cifs_unicode.o nterr.o cifsencrypt.o \
10+
cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
1111
readdir.o ioctl.o sess.o export.o unc.o winucase.o \
1212
smb2ops.o smb2maperror.o smb2transport.o \
1313
smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \

fs/cifs/cached_dir.c

Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Functions to handle the cached directory entries
4+
*
5+
* Copyright (c) 2022, Ronnie Sahlberg <[email protected]>
6+
*/
7+
8+
#include "cifsglob.h"
9+
#include "cifsproto.h"
10+
#include "cifs_debug.h"
11+
#include "smb2proto.h"
12+
#include "cached_dir.h"
13+
14+
/*
15+
* Open the and cache a directory handle.
16+
* If error then *cfid is not initialized.
17+
*/
18+
int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
19+
const char *path,
20+
struct cifs_sb_info *cifs_sb,
21+
bool lookup_only, struct cached_fid **ret_cfid)
22+
{
23+
struct cifs_ses *ses;
24+
struct TCP_Server_Info *server;
25+
struct cifs_open_parms oparms;
26+
struct smb2_create_rsp *o_rsp = NULL;
27+
struct smb2_query_info_rsp *qi_rsp = NULL;
28+
int resp_buftype[2];
29+
struct smb_rqst rqst[2];
30+
struct kvec rsp_iov[2];
31+
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
32+
struct kvec qi_iov[1];
33+
int rc, flags = 0;
34+
__le16 utf16_path = 0; /* Null - since an open of top of share */
35+
u8 oplock = SMB2_OPLOCK_LEVEL_II;
36+
struct cifs_fid *pfid;
37+
struct dentry *dentry;
38+
struct cached_fid *cfid;
39+
40+
if (tcon == NULL || tcon->nohandlecache ||
41+
is_smb1_server(tcon->ses->server))
42+
return -EOPNOTSUPP;
43+
44+
ses = tcon->ses;
45+
server = ses->server;
46+
47+
if (cifs_sb->root == NULL)
48+
return -ENOENT;
49+
50+
if (strlen(path))
51+
return -ENOENT;
52+
53+
dentry = cifs_sb->root;
54+
55+
cfid = tcon->cfid;
56+
mutex_lock(&cfid->fid_mutex);
57+
if (cfid->is_valid) {
58+
cifs_dbg(FYI, "found a cached root file handle\n");
59+
*ret_cfid = cfid;
60+
kref_get(&cfid->refcount);
61+
mutex_unlock(&cfid->fid_mutex);
62+
return 0;
63+
}
64+
65+
/*
66+
* We do not hold the lock for the open because in case
67+
* SMB2_open needs to reconnect, it will end up calling
68+
* cifs_mark_open_files_invalid() which takes the lock again
69+
* thus causing a deadlock
70+
*/
71+
mutex_unlock(&cfid->fid_mutex);
72+
73+
if (lookup_only)
74+
return -ENOENT;
75+
76+
if (smb3_encryption_required(tcon))
77+
flags |= CIFS_TRANSFORM_REQ;
78+
79+
if (!server->ops->new_lease_key)
80+
return -EIO;
81+
82+
pfid = &cfid->fid;
83+
server->ops->new_lease_key(pfid);
84+
85+
memset(rqst, 0, sizeof(rqst));
86+
resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
87+
memset(rsp_iov, 0, sizeof(rsp_iov));
88+
89+
/* Open */
90+
memset(&open_iov, 0, sizeof(open_iov));
91+
rqst[0].rq_iov = open_iov;
92+
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
93+
94+
oparms.tcon = tcon;
95+
oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
96+
oparms.desired_access = FILE_READ_ATTRIBUTES;
97+
oparms.disposition = FILE_OPEN;
98+
oparms.fid = pfid;
99+
oparms.reconnect = false;
100+
101+
rc = SMB2_open_init(tcon, server,
102+
&rqst[0], &oplock, &oparms, &utf16_path);
103+
if (rc)
104+
goto oshr_free;
105+
smb2_set_next_command(tcon, &rqst[0]);
106+
107+
memset(&qi_iov, 0, sizeof(qi_iov));
108+
rqst[1].rq_iov = qi_iov;
109+
rqst[1].rq_nvec = 1;
110+
111+
rc = SMB2_query_info_init(tcon, server,
112+
&rqst[1], COMPOUND_FID,
113+
COMPOUND_FID, FILE_ALL_INFORMATION,
114+
SMB2_O_INFO_FILE, 0,
115+
sizeof(struct smb2_file_all_info) +
116+
PATH_MAX * 2, 0, NULL);
117+
if (rc)
118+
goto oshr_free;
119+
120+
smb2_set_related(&rqst[1]);
121+
122+
rc = compound_send_recv(xid, ses, server,
123+
flags, 2, rqst,
124+
resp_buftype, rsp_iov);
125+
mutex_lock(&cfid->fid_mutex);
126+
127+
/*
128+
* Now we need to check again as the cached root might have
129+
* been successfully re-opened from a concurrent process
130+
*/
131+
132+
if (cfid->is_valid) {
133+
/* work was already done */
134+
135+
/* stash fids for close() later */
136+
struct cifs_fid fid = {
137+
.persistent_fid = pfid->persistent_fid,
138+
.volatile_fid = pfid->volatile_fid,
139+
};
140+
141+
/*
142+
* caller expects this func to set the fid in cfid to valid
143+
* cached root, so increment the refcount.
144+
*/
145+
kref_get(&cfid->refcount);
146+
147+
mutex_unlock(&cfid->fid_mutex);
148+
149+
if (rc == 0) {
150+
/* close extra handle outside of crit sec */
151+
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
152+
}
153+
rc = 0;
154+
goto oshr_free;
155+
}
156+
157+
/* Cached root is still invalid, continue normaly */
158+
159+
if (rc) {
160+
if (rc == -EREMCHG) {
161+
tcon->need_reconnect = true;
162+
pr_warn_once("server share %s deleted\n",
163+
tcon->treeName);
164+
}
165+
goto oshr_exit;
166+
}
167+
168+
atomic_inc(&tcon->num_remote_opens);
169+
170+
o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
171+
oparms.fid->persistent_fid = o_rsp->PersistentFileId;
172+
oparms.fid->volatile_fid = o_rsp->VolatileFileId;
173+
#ifdef CONFIG_CIFS_DEBUG2
174+
oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
175+
#endif /* CIFS_DEBUG2 */
176+
177+
cfid->tcon = tcon;
178+
cfid->is_valid = true;
179+
cfid->dentry = dentry;
180+
dget(dentry);
181+
kref_init(&cfid->refcount);
182+
183+
/* BB TBD check to see if oplock level check can be removed below */
184+
if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
185+
/*
186+
* See commit 2f94a3125b87. Increment the refcount when we
187+
* get a lease for root, release it if lease break occurs
188+
*/
189+
kref_get(&cfid->refcount);
190+
cfid->has_lease = true;
191+
smb2_parse_contexts(server, o_rsp,
192+
&oparms.fid->epoch,
193+
oparms.fid->lease_key, &oplock,
194+
NULL, NULL);
195+
} else
196+
goto oshr_exit;
197+
198+
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
199+
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
200+
goto oshr_exit;
201+
if (!smb2_validate_and_copy_iov(
202+
le16_to_cpu(qi_rsp->OutputBufferOffset),
203+
sizeof(struct smb2_file_all_info),
204+
&rsp_iov[1], sizeof(struct smb2_file_all_info),
205+
(char *)&cfid->file_all_info))
206+
cfid->file_all_info_is_valid = true;
207+
208+
cfid->time = jiffies;
209+
210+
oshr_exit:
211+
mutex_unlock(&cfid->fid_mutex);
212+
oshr_free:
213+
SMB2_open_free(&rqst[0]);
214+
SMB2_query_info_free(&rqst[1]);
215+
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
216+
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
217+
if (rc == 0)
218+
*ret_cfid = cfid;
219+
220+
return rc;
221+
}
222+
223+
int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
224+
struct dentry *dentry,
225+
struct cached_fid **ret_cfid)
226+
{
227+
struct cached_fid *cfid;
228+
229+
cfid = tcon->cfid;
230+
231+
mutex_lock(&cfid->fid_mutex);
232+
if (cfid->dentry == dentry) {
233+
cifs_dbg(FYI, "found a cached root file handle by dentry\n");
234+
*ret_cfid = cfid;
235+
kref_get(&cfid->refcount);
236+
mutex_unlock(&cfid->fid_mutex);
237+
return 0;
238+
}
239+
mutex_unlock(&cfid->fid_mutex);
240+
return -ENOENT;
241+
}
242+
243+
static void
244+
smb2_close_cached_fid(struct kref *ref)
245+
{
246+
struct cached_fid *cfid = container_of(ref, struct cached_fid,
247+
refcount);
248+
struct cached_dirent *dirent, *q;
249+
250+
if (cfid->is_valid) {
251+
cifs_dbg(FYI, "clear cached root file handle\n");
252+
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
253+
cfid->fid.volatile_fid);
254+
}
255+
256+
/*
257+
* We only check validity above to send SMB2_close,
258+
* but we still need to invalidate these entries
259+
* when this function is called
260+
*/
261+
cfid->is_valid = false;
262+
cfid->file_all_info_is_valid = false;
263+
cfid->has_lease = false;
264+
if (cfid->dentry) {
265+
dput(cfid->dentry);
266+
cfid->dentry = NULL;
267+
}
268+
/*
269+
* Delete all cached dirent names
270+
*/
271+
mutex_lock(&cfid->dirents.de_mutex);
272+
list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
273+
list_del(&dirent->entry);
274+
kfree(dirent->name);
275+
kfree(dirent);
276+
}
277+
cfid->dirents.is_valid = 0;
278+
cfid->dirents.is_failed = 0;
279+
cfid->dirents.ctx = NULL;
280+
cfid->dirents.pos = 0;
281+
mutex_unlock(&cfid->dirents.de_mutex);
282+
283+
}
284+
285+
void close_cached_dir(struct cached_fid *cfid)
286+
{
287+
mutex_lock(&cfid->fid_mutex);
288+
kref_put(&cfid->refcount, smb2_close_cached_fid);
289+
mutex_unlock(&cfid->fid_mutex);
290+
}
291+
292+
void close_cached_dir_lease_locked(struct cached_fid *cfid)
293+
{
294+
if (cfid->has_lease) {
295+
cfid->has_lease = false;
296+
kref_put(&cfid->refcount, smb2_close_cached_fid);
297+
}
298+
}
299+
300+
void close_cached_dir_lease(struct cached_fid *cfid)
301+
{
302+
mutex_lock(&cfid->fid_mutex);
303+
close_cached_dir_lease_locked(cfid);
304+
mutex_unlock(&cfid->fid_mutex);
305+
}
306+
307+
/*
308+
* Called from cifs_kill_sb when we unmount a share
309+
*/
310+
void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
311+
{
312+
struct rb_root *root = &cifs_sb->tlink_tree;
313+
struct rb_node *node;
314+
struct cached_fid *cfid;
315+
struct cifs_tcon *tcon;
316+
struct tcon_link *tlink;
317+
318+
for (node = rb_first(root); node; node = rb_next(node)) {
319+
tlink = rb_entry(node, struct tcon_link, tl_rbnode);
320+
tcon = tlink_tcon(tlink);
321+
if (IS_ERR(tcon))
322+
continue;
323+
cfid = tcon->cfid;
324+
mutex_lock(&cfid->fid_mutex);
325+
if (cfid->dentry) {
326+
dput(cfid->dentry);
327+
cfid->dentry = NULL;
328+
}
329+
mutex_unlock(&cfid->fid_mutex);
330+
}
331+
}
332+
333+
/*
334+
* Invalidate and close all cached dirs when a TCON has been reset
335+
* due to a session loss.
336+
*/
337+
void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
338+
{
339+
mutex_lock(&tcon->cfid->fid_mutex);
340+
tcon->cfid->is_valid = false;
341+
/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
342+
close_cached_dir_lease_locked(tcon->cfid);
343+
memset(&tcon->cfid->fid, 0, sizeof(struct cifs_fid));
344+
mutex_unlock(&tcon->cfid->fid_mutex);
345+
}
346+
347+
static void
348+
smb2_cached_lease_break(struct work_struct *work)
349+
{
350+
struct cached_fid *cfid = container_of(work,
351+
struct cached_fid, lease_break);
352+
353+
close_cached_dir_lease(cfid);
354+
}
355+
356+
int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
357+
{
358+
if (tcon->cfid->is_valid &&
359+
!memcmp(lease_key,
360+
tcon->cfid->fid.lease_key,
361+
SMB2_LEASE_KEY_SIZE)) {
362+
tcon->cfid->time = 0;
363+
INIT_WORK(&tcon->cfid->lease_break,
364+
smb2_cached_lease_break);
365+
queue_work(cifsiod_wq,
366+
&tcon->cfid->lease_break);
367+
return true;
368+
}
369+
return false;
370+
}
371+
372+
struct cached_fid *init_cached_dir(void)
373+
{
374+
struct cached_fid *cfid;
375+
376+
cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
377+
if (!cfid)
378+
return NULL;
379+
INIT_LIST_HEAD(&cfid->dirents.entries);
380+
mutex_init(&cfid->dirents.de_mutex);
381+
mutex_init(&cfid->fid_mutex);
382+
return cfid;
383+
}
384+
385+
void free_cached_dir(struct cifs_tcon *tcon)
386+
{
387+
kfree(tcon->cfid);
388+
}

0 commit comments

Comments
 (0)