Skip to content

Commit df0a8a1

Browse files
Paulo Alcantaragregkh
authored andcommitted
smb: client: add support for WSL reparse points
[ Upstream commit 5a4b09e ] Add support for creating special files via WSL reparse points when using 'reparse=wsl' mount option. They're faster than NFS reparse points because they don't require extra roundtrips to figure out what ->d_type a specific dirent is as such information is already stored in query dir responses and then making getdents() calls faster. Signed-off-by: Paulo Alcantara <[email protected]> Signed-off-by: Steve French <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 831ffbd commit df0a8a1

File tree

10 files changed

+210
-20
lines changed

10 files changed

+210
-20
lines changed

fs/smb/client/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,7 @@ struct cifs_open_parms {
13791379
umode_t mode;
13801380
bool reconnect:1;
13811381
bool replay:1; /* indicates that this open is for a replay */
1382+
struct kvec *ea_cctx;
13821383
};
13831384

13841385
struct cifs_fid {

fs/smb/client/fs_context.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ static int parse_reparse_flavor(struct fs_context *fc, char *value,
318318
ctx->reparse_type = CIFS_REPARSE_TYPE_NFS;
319319
break;
320320
case Opt_reparse_wsl:
321-
cifs_errorf(fc, "unsupported reparse= option: %s\n", value);
322-
return 1;
321+
ctx->reparse_type = CIFS_REPARSE_TYPE_WSL;
322+
break;
323323
default:
324324
cifs_errorf(fc, "bad reparse= option: %s\n", value);
325325
return 1;

fs/smb/client/reparse.c

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "cifsproto.h"
1212
#include "cifs_unicode.h"
1313
#include "cifs_debug.h"
14+
#include "fs_context.h"
1415
#include "reparse.h"
1516

1617
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
@@ -68,7 +69,7 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
6869
iov.iov_base = buf;
6970
iov.iov_len = len;
7071
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
71-
tcon, full_path, &iov);
72+
tcon, full_path, &iov, NULL);
7273
if (!IS_ERR(new))
7374
d_instantiate(dentry, new);
7475
else
@@ -114,9 +115,9 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
114115
return 0;
115116
}
116117

117-
int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
118-
struct dentry *dentry, struct cifs_tcon *tcon,
119-
const char *full_path, umode_t mode, dev_t dev)
118+
static int mknod_nfs(unsigned int xid, struct inode *inode,
119+
struct dentry *dentry, struct cifs_tcon *tcon,
120+
const char *full_path, umode_t mode, dev_t dev)
120121
{
121122
struct cifs_open_info_data data;
122123
struct reparse_posix_data *p;
@@ -136,12 +137,171 @@ int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
136137
};
137138

138139
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
139-
tcon, full_path, &iov);
140+
tcon, full_path, &iov, NULL);
141+
if (!IS_ERR(new))
142+
d_instantiate(dentry, new);
143+
else
144+
rc = PTR_ERR(new);
145+
cifs_free_open_info(&data);
146+
return rc;
147+
}
148+
149+
static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
150+
mode_t mode, struct kvec *iov)
151+
{
152+
u32 tag;
153+
154+
switch ((tag = reparse_mode_wsl_tag(mode))) {
155+
case IO_REPARSE_TAG_LX_BLK:
156+
case IO_REPARSE_TAG_LX_CHR:
157+
case IO_REPARSE_TAG_LX_FIFO:
158+
case IO_REPARSE_TAG_AF_UNIX:
159+
break;
160+
default:
161+
return -EOPNOTSUPP;
162+
}
163+
164+
buf->ReparseTag = cpu_to_le32(tag);
165+
buf->Reserved = 0;
166+
buf->ReparseDataLength = 0;
167+
iov->iov_base = buf;
168+
iov->iov_len = sizeof(*buf);
169+
return 0;
170+
}
171+
172+
static struct smb2_create_ea_ctx *ea_create_context(u32 dlen, size_t *cc_len)
173+
{
174+
struct smb2_create_ea_ctx *cc;
175+
176+
*cc_len = round_up(sizeof(*cc) + dlen, 8);
177+
cc = kzalloc(*cc_len, GFP_KERNEL);
178+
if (!cc)
179+
return ERR_PTR(-ENOMEM);
180+
181+
cc->ctx.NameOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx,
182+
name));
183+
cc->ctx.NameLength = cpu_to_le16(4);
184+
memcpy(cc->name, SMB2_CREATE_EA_BUFFER, strlen(SMB2_CREATE_EA_BUFFER));
185+
cc->ctx.DataOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ea));
186+
cc->ctx.DataLength = cpu_to_le32(dlen);
187+
return cc;
188+
}
189+
190+
struct wsl_xattr {
191+
const char *name;
192+
__le64 value;
193+
u16 size;
194+
u32 next;
195+
};
196+
197+
static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
198+
dev_t _dev, struct kvec *iov)
199+
{
200+
struct smb2_file_full_ea_info *ea;
201+
struct smb2_create_ea_ctx *cc;
202+
struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
203+
__le64 uid = cpu_to_le64(from_kuid(current_user_ns(), ctx->linux_uid));
204+
__le64 gid = cpu_to_le64(from_kgid(current_user_ns(), ctx->linux_gid));
205+
__le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
206+
__le64 mode = cpu_to_le64(_mode);
207+
struct wsl_xattr xattrs[] = {
208+
{ .name = "$LXUID", .value = uid, .size = 4, },
209+
{ .name = "$LXGID", .value = gid, .size = 4, },
210+
{ .name = "$LXMOD", .value = mode, .size = 4, },
211+
{ .name = "$LXDEV", .value = dev, .size = 8, },
212+
};
213+
size_t cc_len;
214+
u32 dlen = 0, next = 0;
215+
int i, num_xattrs;
216+
u8 name_size = strlen(xattrs[0].name) + 1;
217+
218+
memset(iov, 0, sizeof(*iov));
219+
220+
/* Exclude $LXDEV xattr for sockets and fifos */
221+
if (S_ISSOCK(_mode) || S_ISFIFO(_mode))
222+
num_xattrs = ARRAY_SIZE(xattrs) - 1;
223+
else
224+
num_xattrs = ARRAY_SIZE(xattrs);
225+
226+
for (i = 0; i < num_xattrs; i++) {
227+
xattrs[i].next = ALIGN(sizeof(*ea) + name_size +
228+
xattrs[i].size, 4);
229+
dlen += xattrs[i].next;
230+
}
231+
232+
cc = ea_create_context(dlen, &cc_len);
233+
if (!cc)
234+
return PTR_ERR(cc);
235+
236+
ea = &cc->ea;
237+
for (i = 0; i < num_xattrs; i++) {
238+
ea = (void *)((u8 *)ea + next);
239+
next = xattrs[i].next;
240+
ea->next_entry_offset = cpu_to_le32(next);
241+
242+
ea->ea_name_length = name_size - 1;
243+
ea->ea_value_length = cpu_to_le16(xattrs[i].size);
244+
memcpy(ea->ea_data, xattrs[i].name, name_size);
245+
memcpy(&ea->ea_data[name_size],
246+
&xattrs[i].value, xattrs[i].size);
247+
}
248+
ea->next_entry_offset = 0;
249+
250+
iov->iov_base = cc;
251+
iov->iov_len = cc_len;
252+
return 0;
253+
}
254+
255+
static int mknod_wsl(unsigned int xid, struct inode *inode,
256+
struct dentry *dentry, struct cifs_tcon *tcon,
257+
const char *full_path, umode_t mode, dev_t dev)
258+
{
259+
struct cifs_open_info_data data;
260+
struct reparse_data_buffer buf;
261+
struct inode *new;
262+
struct kvec reparse_iov, xattr_iov;
263+
int rc;
264+
265+
rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
266+
if (rc)
267+
return rc;
268+
269+
rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
270+
if (rc)
271+
return rc;
272+
273+
data = (struct cifs_open_info_data) {
274+
.reparse_point = true,
275+
.reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
276+
};
277+
278+
new = smb2_get_reparse_inode(&data, inode->i_sb,
279+
xid, tcon, full_path,
280+
&reparse_iov, &xattr_iov);
140281
if (!IS_ERR(new))
141282
d_instantiate(dentry, new);
142283
else
143284
rc = PTR_ERR(new);
144285
cifs_free_open_info(&data);
286+
kfree(xattr_iov.iov_base);
287+
return rc;
288+
}
289+
290+
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
291+
struct dentry *dentry, struct cifs_tcon *tcon,
292+
const char *full_path, umode_t mode, dev_t dev)
293+
{
294+
struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
295+
int rc = -EOPNOTSUPP;
296+
297+
switch (ctx->reparse_type) {
298+
case CIFS_REPARSE_TYPE_NFS:
299+
rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
300+
break;
301+
case CIFS_REPARSE_TYPE_WSL:
302+
rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
303+
break;
304+
}
145305
return rc;
146306
}
147307

fs/smb/client/reparse.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ static inline u64 reparse_mode_nfs_type(mode_t mode)
2828
return 0;
2929
}
3030

31+
static inline u32 reparse_mode_wsl_tag(mode_t mode)
32+
{
33+
switch (mode & S_IFMT) {
34+
case S_IFBLK: return IO_REPARSE_TAG_LX_BLK;
35+
case S_IFCHR: return IO_REPARSE_TAG_LX_CHR;
36+
case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO;
37+
case S_IFSOCK: return IO_REPARSE_TAG_AF_UNIX;
38+
}
39+
return 0;
40+
}
41+
3142
/*
3243
* Match a reparse point inode if reparse tag and ctime haven't changed.
3344
*
@@ -64,7 +75,7 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
6475
int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
6576
struct dentry *dentry, struct cifs_tcon *tcon,
6677
const char *full_path, const char *symname);
67-
int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
78+
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
6879
struct dentry *dentry, struct cifs_tcon *tcon,
6980
const char *full_path, umode_t mode, dev_t dev);
7081
int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,

fs/smb/client/smb2inode.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
10601060
const unsigned int xid,
10611061
struct cifs_tcon *tcon,
10621062
const char *full_path,
1063-
struct kvec *iov)
1063+
struct kvec *reparse_iov,
1064+
struct kvec *xattr_iov)
10641065
{
10651066
struct cifs_open_parms oparms;
10661067
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -1077,8 +1078,11 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
10771078
FILE_CREATE,
10781079
CREATE_NOT_DIR | OPEN_REPARSE_POINT,
10791080
ACL_NO_MODE);
1081+
if (xattr_iov)
1082+
oparms.ea_cctx = xattr_iov;
1083+
10801084
cmds[0] = SMB2_OP_SET_REPARSE;
1081-
in_iov[0] = *iov;
1085+
in_iov[0] = *reparse_iov;
10821086
in_iov[1].iov_base = data;
10831087
in_iov[1].iov_len = sizeof(*data);
10841088

fs/smb/client/smb2ops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5038,7 +5038,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
50385038
rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
50395039
full_path, mode, dev);
50405040
} else {
5041-
rc = smb2_make_nfs_node(xid, inode, dentry, tcon,
5041+
rc = smb2_mknod_reparse(xid, inode, dentry, tcon,
50425042
full_path, mode, dev);
50435043
}
50445044
return rc;

fs/smb/client/smb2pdu.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,6 +2732,17 @@ add_query_id_context(struct kvec *iov, unsigned int *num_iovec)
27322732
return 0;
27332733
}
27342734

2735+
static void add_ea_context(struct cifs_open_parms *oparms,
2736+
struct kvec *rq_iov, unsigned int *num_iovs)
2737+
{
2738+
struct kvec *iov = oparms->ea_cctx;
2739+
2740+
if (iov && iov->iov_base && iov->iov_len) {
2741+
rq_iov[(*num_iovs)++] = *iov;
2742+
memset(iov, 0, sizeof(*iov));
2743+
}
2744+
}
2745+
27352746
static int
27362747
alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
27372748
const char *treename, const __le16 *path)
@@ -3098,6 +3109,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
30983109
}
30993110

31003111
add_query_id_context(iov, &n_iov);
3112+
add_ea_context(oparms, iov, &n_iov);
31013113

31023114
if (n_iov > 2) {
31033115
/*

fs/smb/client/smb2pdu.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ struct share_redirect_error_context_rsp {
117117
* [4] : posix context
118118
* [5] : time warp context
119119
* [6] : query id context
120-
* [7] : compound padding
120+
* [7] : create ea context
121+
* [8] : compound padding
121122
*/
122-
#define SMB2_CREATE_IOV_SIZE 8
123+
#define SMB2_CREATE_IOV_SIZE 9
123124

124125
/*
125126
* Maximum size of a SMB2_CREATE response is 64 (smb2 header) +
@@ -413,4 +414,10 @@ struct smb2_posix_info_parsed {
413414
const u8 *name;
414415
};
415416

417+
struct smb2_create_ea_ctx {
418+
struct create_context ctx;
419+
__u8 name[8];
420+
struct smb2_file_full_ea_info ea;
421+
} __packed;
422+
416423
#endif /* _SMB2PDU_H */

fs/smb/client/smb2proto.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
6161
const unsigned int xid,
6262
struct cifs_tcon *tcon,
6363
const char *full_path,
64-
struct kvec *iov);
64+
struct kvec *reparse_iov,
65+
struct kvec *xattr_iov);
6566
int smb2_query_reparse_point(const unsigned int xid,
6667
struct cifs_tcon *tcon,
6768
struct cifs_sb_info *cifs_sb,

fs/smb/common/smbfsctl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,6 @@
158158
#define IO_REPARSE_TAG_LX_CHR 0x80000025
159159
#define IO_REPARSE_TAG_LX_BLK 0x80000026
160160

161-
#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D)
162-
#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023)
163-
#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024)
164-
#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025)
165-
#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026)
166-
167161
/* fsctl flags */
168162
/* If Flags is set to this value, the request is an FSCTL not ioctl request */
169163
#define SMB2_0_IOCTL_IS_FSCTL 0x00000001

0 commit comments

Comments
 (0)