Skip to content

Commit 05b98fd

Browse files
Ronnie SahlbergSteve French
authored andcommitted
cifs: Move cached-dir functions into a separate file
Also rename crfid to cfid to have consistent naming for this variable. This commit does not change any logic. Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent cd04345 commit 05b98fd

File tree

15 files changed

+411
-351
lines changed

15 files changed

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

fs/cifs/cached_dir.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Functions to handle the cached directory entries
4+
*
5+
* Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
6+
*/
7+
8+
#ifndef _CACHED_DIR_H
9+
#define _CACHED_DIR_H
10+
11+
12+
extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
13+
const char *path,
14+
struct cifs_sb_info *cifs_sb,
15+
struct cached_fid **cfid);
16+
extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
17+
struct dentry *dentry,
18+
struct cached_fid **cfid);
19+
extern void close_cached_dir(struct cached_fid *cfid);
20+
extern void close_cached_dir_lease(struct cached_fid *cfid);
21+
extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
22+
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
23+
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
24+
extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
25+
26+
#endif /* _CACHED_DIR_H */

0 commit comments

Comments
 (0)