Loading...
Note: File does not exist in v3.15.
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
4 */
5
6#include <linux/fs.h>
7#include <linux/stat.h>
8#include <linux/slab.h>
9#include "cifsglob.h"
10#include "smb2proto.h"
11#include "cifsproto.h"
12#include "cifs_unicode.h"
13#include "cifs_debug.h"
14#include "fs_context.h"
15#include "reparse.h"
16
17int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
18 struct dentry *dentry, struct cifs_tcon *tcon,
19 const char *full_path, const char *symname)
20{
21 struct reparse_symlink_data_buffer *buf = NULL;
22 struct cifs_open_info_data data;
23 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
24 struct inode *new;
25 struct kvec iov;
26 __le16 *path;
27 char *sym, sep = CIFS_DIR_SEP(cifs_sb);
28 u16 len, plen;
29 int rc = 0;
30
31 sym = kstrdup(symname, GFP_KERNEL);
32 if (!sym)
33 return -ENOMEM;
34
35 data = (struct cifs_open_info_data) {
36 .reparse_point = true,
37 .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
38 .symlink_target = sym,
39 };
40
41 convert_delimiter(sym, sep);
42 path = cifs_convert_path_to_utf16(sym, cifs_sb);
43 if (!path) {
44 rc = -ENOMEM;
45 goto out;
46 }
47
48 plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
49 len = sizeof(*buf) + plen * 2;
50 buf = kzalloc(len, GFP_KERNEL);
51 if (!buf) {
52 rc = -ENOMEM;
53 goto out;
54 }
55
56 buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
57 buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
58 buf->SubstituteNameOffset = cpu_to_le16(plen);
59 buf->SubstituteNameLength = cpu_to_le16(plen);
60 memcpy(&buf->PathBuffer[plen], path, plen);
61 buf->PrintNameOffset = 0;
62 buf->PrintNameLength = cpu_to_le16(plen);
63 memcpy(buf->PathBuffer, path, plen);
64 buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
65 if (*sym != sep)
66 buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
67
68 convert_delimiter(sym, '/');
69 iov.iov_base = buf;
70 iov.iov_len = len;
71 new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
72 tcon, full_path, &iov, NULL);
73 if (!IS_ERR(new))
74 d_instantiate(dentry, new);
75 else
76 rc = PTR_ERR(new);
77out:
78 kfree(path);
79 cifs_free_open_info(&data);
80 kfree(buf);
81 return rc;
82}
83
84static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
85 mode_t mode, dev_t dev,
86 struct kvec *iov)
87{
88 u64 type;
89 u16 len, dlen;
90
91 len = sizeof(*buf);
92
93 switch ((type = reparse_mode_nfs_type(mode))) {
94 case NFS_SPECFILE_BLK:
95 case NFS_SPECFILE_CHR:
96 dlen = sizeof(__le64);
97 break;
98 case NFS_SPECFILE_FIFO:
99 case NFS_SPECFILE_SOCK:
100 dlen = 0;
101 break;
102 default:
103 return -EOPNOTSUPP;
104 }
105
106 buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
107 buf->Reserved = 0;
108 buf->InodeType = cpu_to_le64(type);
109 buf->ReparseDataLength = cpu_to_le16(len + dlen -
110 sizeof(struct reparse_data_buffer));
111 *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
112 MINOR(dev));
113 iov->iov_base = buf;
114 iov->iov_len = len + dlen;
115 return 0;
116}
117
118static 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)
121{
122 struct cifs_open_info_data data;
123 struct reparse_posix_data *p;
124 struct inode *new;
125 struct kvec iov;
126 __u8 buf[sizeof(*p) + sizeof(__le64)];
127 int rc;
128
129 p = (struct reparse_posix_data *)buf;
130 rc = nfs_set_reparse_buf(p, mode, dev, &iov);
131 if (rc)
132 return rc;
133
134 data = (struct cifs_open_info_data) {
135 .reparse_point = true,
136 .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
137 };
138
139 new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
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
149static 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
172static 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
190struct wsl_xattr {
191 const char *name;
192 __le64 value;
193 u16 size;
194 u32 next;
195};
196
197static 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 = SMB2_WSL_XATTR_UID, .value = uid, .size = SMB2_WSL_XATTR_UID_SIZE, },
209 { .name = SMB2_WSL_XATTR_GID, .value = gid, .size = SMB2_WSL_XATTR_GID_SIZE, },
210 { .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
211 { .name = SMB2_WSL_XATTR_DEV, .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
212 };
213 size_t cc_len;
214 u32 dlen = 0, next = 0;
215 int i, num_xattrs;
216 u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 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 (IS_ERR(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
255static 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 smb2_create_ea_ctx *cc;
262 struct inode *new;
263 unsigned int len;
264 struct kvec reparse_iov, xattr_iov;
265 int rc;
266
267 rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
268 if (rc)
269 return rc;
270
271 rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
272 if (rc)
273 return rc;
274
275 data = (struct cifs_open_info_data) {
276 .reparse_point = true,
277 .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
278 };
279
280 cc = xattr_iov.iov_base;
281 len = le32_to_cpu(cc->ctx.DataLength);
282 memcpy(data.wsl.eas, &cc->ea, len);
283 data.wsl.eas_len = len;
284
285 new = smb2_get_reparse_inode(&data, inode->i_sb,
286 xid, tcon, full_path,
287 &reparse_iov, &xattr_iov);
288 if (!IS_ERR(new))
289 d_instantiate(dentry, new);
290 else
291 rc = PTR_ERR(new);
292 cifs_free_open_info(&data);
293 kfree(xattr_iov.iov_base);
294 return rc;
295}
296
297int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
298 struct dentry *dentry, struct cifs_tcon *tcon,
299 const char *full_path, umode_t mode, dev_t dev)
300{
301 struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
302 int rc = -EOPNOTSUPP;
303
304 switch (ctx->reparse_type) {
305 case CIFS_REPARSE_TYPE_NFS:
306 rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
307 break;
308 case CIFS_REPARSE_TYPE_WSL:
309 rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
310 break;
311 }
312 return rc;
313}
314
315/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
316static int parse_reparse_posix(struct reparse_posix_data *buf,
317 struct cifs_sb_info *cifs_sb,
318 struct cifs_open_info_data *data)
319{
320 unsigned int len;
321 u64 type;
322
323 switch ((type = le64_to_cpu(buf->InodeType))) {
324 case NFS_SPECFILE_LNK:
325 len = le16_to_cpu(buf->ReparseDataLength);
326 data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
327 len, true,
328 cifs_sb->local_nls);
329 if (!data->symlink_target)
330 return -ENOMEM;
331 convert_delimiter(data->symlink_target, '/');
332 cifs_dbg(FYI, "%s: target path: %s\n",
333 __func__, data->symlink_target);
334 break;
335 case NFS_SPECFILE_CHR:
336 case NFS_SPECFILE_BLK:
337 case NFS_SPECFILE_FIFO:
338 case NFS_SPECFILE_SOCK:
339 break;
340 default:
341 cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
342 __func__, type);
343 return -EOPNOTSUPP;
344 }
345 return 0;
346}
347
348static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
349 u32 plen, bool unicode,
350 struct cifs_sb_info *cifs_sb,
351 struct cifs_open_info_data *data)
352{
353 unsigned int len;
354 unsigned int offs;
355
356 /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
357
358 offs = le16_to_cpu(sym->SubstituteNameOffset);
359 len = le16_to_cpu(sym->SubstituteNameLength);
360 if (offs + 20 > plen || offs + len + 20 > plen) {
361 cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
362 return -EIO;
363 }
364
365 data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
366 len, unicode,
367 cifs_sb->local_nls);
368 if (!data->symlink_target)
369 return -ENOMEM;
370
371 convert_delimiter(data->symlink_target, '/');
372 cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
373
374 return 0;
375}
376
377int parse_reparse_point(struct reparse_data_buffer *buf,
378 u32 plen, struct cifs_sb_info *cifs_sb,
379 bool unicode, struct cifs_open_info_data *data)
380{
381 data->reparse.buf = buf;
382
383 /* See MS-FSCC 2.1.2 */
384 switch (le32_to_cpu(buf->ReparseTag)) {
385 case IO_REPARSE_TAG_NFS:
386 return parse_reparse_posix((struct reparse_posix_data *)buf,
387 cifs_sb, data);
388 case IO_REPARSE_TAG_SYMLINK:
389 return parse_reparse_symlink(
390 (struct reparse_symlink_data_buffer *)buf,
391 plen, unicode, cifs_sb, data);
392 case IO_REPARSE_TAG_LX_SYMLINK:
393 case IO_REPARSE_TAG_AF_UNIX:
394 case IO_REPARSE_TAG_LX_FIFO:
395 case IO_REPARSE_TAG_LX_CHR:
396 case IO_REPARSE_TAG_LX_BLK:
397 return 0;
398 default:
399 cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
400 __func__, le32_to_cpu(buf->ReparseTag));
401 return -EOPNOTSUPP;
402 }
403}
404
405int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
406 struct kvec *rsp_iov,
407 struct cifs_open_info_data *data)
408{
409 struct reparse_data_buffer *buf;
410 struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
411 u32 plen = le32_to_cpu(io->OutputCount);
412
413 buf = (struct reparse_data_buffer *)((u8 *)io +
414 le32_to_cpu(io->OutputOffset));
415 return parse_reparse_point(buf, plen, cifs_sb, true, data);
416}
417
418static void wsl_to_fattr(struct cifs_open_info_data *data,
419 struct cifs_sb_info *cifs_sb,
420 u32 tag, struct cifs_fattr *fattr)
421{
422 struct smb2_file_full_ea_info *ea;
423 u32 next = 0;
424
425 switch (tag) {
426 case IO_REPARSE_TAG_LX_SYMLINK:
427 fattr->cf_mode |= S_IFLNK;
428 break;
429 case IO_REPARSE_TAG_LX_FIFO:
430 fattr->cf_mode |= S_IFIFO;
431 break;
432 case IO_REPARSE_TAG_AF_UNIX:
433 fattr->cf_mode |= S_IFSOCK;
434 break;
435 case IO_REPARSE_TAG_LX_CHR:
436 fattr->cf_mode |= S_IFCHR;
437 break;
438 case IO_REPARSE_TAG_LX_BLK:
439 fattr->cf_mode |= S_IFBLK;
440 break;
441 }
442
443 if (!data->wsl.eas_len)
444 goto out;
445
446 ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
447 do {
448 const char *name;
449 void *v;
450 u8 nlen;
451
452 ea = (void *)((u8 *)ea + next);
453 next = le32_to_cpu(ea->next_entry_offset);
454 if (!le16_to_cpu(ea->ea_value_length))
455 continue;
456
457 name = ea->ea_data;
458 nlen = ea->ea_name_length;
459 v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1);
460
461 if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen))
462 fattr->cf_uid = wsl_make_kuid(cifs_sb, v);
463 else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))
464 fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
465 else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
466 fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
467 else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
468 fattr->cf_rdev = wsl_mkdev(v);
469 } while (next);
470out:
471 fattr->cf_dtype = S_DT(fattr->cf_mode);
472}
473
474bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
475 struct cifs_fattr *fattr,
476 struct cifs_open_info_data *data)
477{
478 struct reparse_posix_data *buf = data->reparse.posix;
479 u32 tag = data->reparse.tag;
480
481 if (tag == IO_REPARSE_TAG_NFS && buf) {
482 switch (le64_to_cpu(buf->InodeType)) {
483 case NFS_SPECFILE_CHR:
484 fattr->cf_mode |= S_IFCHR;
485 fattr->cf_rdev = reparse_nfs_mkdev(buf);
486 break;
487 case NFS_SPECFILE_BLK:
488 fattr->cf_mode |= S_IFBLK;
489 fattr->cf_rdev = reparse_nfs_mkdev(buf);
490 break;
491 case NFS_SPECFILE_FIFO:
492 fattr->cf_mode |= S_IFIFO;
493 break;
494 case NFS_SPECFILE_SOCK:
495 fattr->cf_mode |= S_IFSOCK;
496 break;
497 case NFS_SPECFILE_LNK:
498 fattr->cf_mode |= S_IFLNK;
499 break;
500 default:
501 WARN_ON_ONCE(1);
502 return false;
503 }
504 goto out;
505 }
506
507 switch (tag) {
508 case IO_REPARSE_TAG_DFS:
509 case IO_REPARSE_TAG_DFSR:
510 case IO_REPARSE_TAG_MOUNT_POINT:
511 /* See cifs_create_junction_fattr() */
512 fattr->cf_mode = S_IFDIR | 0711;
513 break;
514 case IO_REPARSE_TAG_LX_SYMLINK:
515 case IO_REPARSE_TAG_LX_FIFO:
516 case IO_REPARSE_TAG_AF_UNIX:
517 case IO_REPARSE_TAG_LX_CHR:
518 case IO_REPARSE_TAG_LX_BLK:
519 wsl_to_fattr(data, cifs_sb, tag, fattr);
520 break;
521 case 0: /* SMB1 symlink */
522 case IO_REPARSE_TAG_SYMLINK:
523 case IO_REPARSE_TAG_NFS:
524 fattr->cf_mode |= S_IFLNK;
525 break;
526 default:
527 return false;
528 }
529out:
530 fattr->cf_dtype = S_DT(fattr->cf_mode);
531 return true;
532}