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
17 static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
18 const unsigned int xid,
19 const char *full_path,
20 const char *symname,
21 bool *directory);
22
smb2_create_reparse_symlink(const unsigned int xid,struct inode * inode,struct dentry * dentry,struct cifs_tcon * tcon,const char * full_path,const char * symname)23 int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
24 struct dentry *dentry, struct cifs_tcon *tcon,
25 const char *full_path, const char *symname)
26 {
27 struct reparse_symlink_data_buffer *buf = NULL;
28 struct cifs_open_info_data data;
29 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
30 struct inode *new;
31 struct kvec iov;
32 __le16 *path;
33 bool directory;
34 char *sym, sep = CIFS_DIR_SEP(cifs_sb);
35 u16 len, plen;
36 int rc = 0;
37
38 if (strlen(symname) > REPARSE_SYM_PATH_MAX)
39 return -ENAMETOOLONG;
40
41 sym = kstrdup(symname, GFP_KERNEL);
42 if (!sym)
43 return -ENOMEM;
44
45 data = (struct cifs_open_info_data) {
46 .reparse_point = true,
47 .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
48 .symlink_target = sym,
49 };
50
51 convert_delimiter(sym, sep);
52 path = cifs_convert_path_to_utf16(sym, cifs_sb);
53 if (!path) {
54 rc = -ENOMEM;
55 goto out;
56 }
57
58 /*
59 * SMB distinguish between symlink to directory and symlink to file.
60 * They cannot be exchanged (symlink of file type which points to
61 * directory cannot be resolved and vice-versa). Try to detect if
62 * the symlink target could be a directory or not. When detection
63 * fails then treat symlink as a file (non-directory) symlink.
64 */
65 directory = false;
66 rc = detect_directory_symlink_target(cifs_sb, xid, full_path, symname, &directory);
67 if (rc < 0)
68 goto out;
69
70 plen = 2 * UniStrnlen((wchar_t *)path, REPARSE_SYM_PATH_MAX);
71 len = sizeof(*buf) + plen * 2;
72 buf = kzalloc(len, GFP_KERNEL);
73 if (!buf) {
74 rc = -ENOMEM;
75 goto out;
76 }
77
78 buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
79 buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
80 buf->SubstituteNameOffset = cpu_to_le16(plen);
81 buf->SubstituteNameLength = cpu_to_le16(plen);
82 memcpy(&buf->PathBuffer[plen], path, plen);
83 buf->PrintNameOffset = 0;
84 buf->PrintNameLength = cpu_to_le16(plen);
85 memcpy(buf->PathBuffer, path, plen);
86 buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
87 if (*sym != sep)
88 buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
89
90 convert_delimiter(sym, '/');
91 iov.iov_base = buf;
92 iov.iov_len = len;
93 new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
94 tcon, full_path, directory,
95 &iov, NULL);
96 if (!IS_ERR(new))
97 d_instantiate(dentry, new);
98 else
99 rc = PTR_ERR(new);
100 out:
101 kfree(path);
102 cifs_free_open_info(&data);
103 kfree(buf);
104 return rc;
105 }
106
detect_directory_symlink_target(struct cifs_sb_info * cifs_sb,const unsigned int xid,const char * full_path,const char * symname,bool * directory)107 static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
108 const unsigned int xid,
109 const char *full_path,
110 const char *symname,
111 bool *directory)
112 {
113 char sep = CIFS_DIR_SEP(cifs_sb);
114 struct cifs_open_parms oparms;
115 struct tcon_link *tlink;
116 struct cifs_tcon *tcon;
117 const char *basename;
118 struct cifs_fid fid;
119 char *resolved_path;
120 int full_path_len;
121 int basename_len;
122 int symname_len;
123 char *path_sep;
124 __u32 oplock;
125 int open_rc;
126
127 /*
128 * First do some simple check. If the original Linux symlink target ends
129 * with slash, or last path component is dot or dot-dot then it is for
130 * sure symlink to the directory.
131 */
132 basename = kbasename(symname);
133 basename_len = strlen(basename);
134 if (basename_len == 0 || /* symname ends with slash */
135 (basename_len == 1 && basename[0] == '.') || /* last component is "." */
136 (basename_len == 2 && basename[0] == '.' && basename[1] == '.')) { /* or ".." */
137 *directory = true;
138 return 0;
139 }
140
141 /*
142 * For absolute symlinks it is not possible to determinate
143 * if it should point to directory or file.
144 */
145 if (symname[0] == '/') {
146 cifs_dbg(FYI,
147 "%s: cannot determinate if the symlink target path '%s' "
148 "is directory or not, creating '%s' as file symlink\n",
149 __func__, symname, full_path);
150 return 0;
151 }
152
153 /*
154 * If it was not detected as directory yet and the symlink is relative
155 * then try to resolve the path on the SMB server, check if the path
156 * exists and determinate if it is a directory or not.
157 */
158
159 full_path_len = strlen(full_path);
160 symname_len = strlen(symname);
161
162 tlink = cifs_sb_tlink(cifs_sb);
163 if (IS_ERR(tlink))
164 return PTR_ERR(tlink);
165
166 resolved_path = kzalloc(full_path_len + symname_len + 1, GFP_KERNEL);
167 if (!resolved_path) {
168 cifs_put_tlink(tlink);
169 return -ENOMEM;
170 }
171
172 /*
173 * Compose the resolved SMB symlink path from the SMB full path
174 * and Linux target symlink path.
175 */
176 memcpy(resolved_path, full_path, full_path_len+1);
177 path_sep = strrchr(resolved_path, sep);
178 if (path_sep)
179 path_sep++;
180 else
181 path_sep = resolved_path;
182 memcpy(path_sep, symname, symname_len+1);
183 if (sep == '\\')
184 convert_delimiter(path_sep, sep);
185
186 tcon = tlink_tcon(tlink);
187 oparms = CIFS_OPARMS(cifs_sb, tcon, resolved_path,
188 FILE_READ_ATTRIBUTES, FILE_OPEN, 0, ACL_NO_MODE);
189 oparms.fid = &fid;
190
191 /* Try to open as a directory (NOT_FILE) */
192 oplock = 0;
193 oparms.create_options = cifs_create_options(cifs_sb,
194 CREATE_NOT_FILE | OPEN_REPARSE_POINT);
195 open_rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
196 if (open_rc == 0) {
197 /* Successful open means that the target path is definitely a directory. */
198 *directory = true;
199 tcon->ses->server->ops->close(xid, tcon, &fid);
200 } else if (open_rc == -ENOTDIR) {
201 /* -ENOTDIR means that the target path is definitely a file. */
202 *directory = false;
203 } else if (open_rc == -ENOENT) {
204 /* -ENOENT means that the target path does not exist. */
205 cifs_dbg(FYI,
206 "%s: symlink target path '%s' does not exist, "
207 "creating '%s' as file symlink\n",
208 __func__, symname, full_path);
209 } else {
210 /* Try to open as a file (NOT_DIR) */
211 oplock = 0;
212 oparms.create_options = cifs_create_options(cifs_sb,
213 CREATE_NOT_DIR | OPEN_REPARSE_POINT);
214 open_rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
215 if (open_rc == 0) {
216 /* Successful open means that the target path is definitely a file. */
217 *directory = false;
218 tcon->ses->server->ops->close(xid, tcon, &fid);
219 } else if (open_rc == -EISDIR) {
220 /* -EISDIR means that the target path is definitely a directory. */
221 *directory = true;
222 } else {
223 /*
224 * This code branch is called when we do not have a permission to
225 * open the resolved_path or some other client/process denied
226 * opening the resolved_path.
227 *
228 * TODO: Try to use ops->query_dir_first on the parent directory
229 * of resolved_path, search for basename of resolved_path and
230 * check if the ATTR_DIRECTORY is set in fi.Attributes. In some
231 * case this could work also when opening of the path is denied.
232 */
233 cifs_dbg(FYI,
234 "%s: cannot determinate if the symlink target path '%s' "
235 "is directory or not, creating '%s' as file symlink\n",
236 __func__, symname, full_path);
237 }
238 }
239
240 kfree(resolved_path);
241 cifs_put_tlink(tlink);
242 return 0;
243 }
244
nfs_set_reparse_buf(struct reparse_posix_data * buf,mode_t mode,dev_t dev,struct kvec * iov)245 static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
246 mode_t mode, dev_t dev,
247 struct kvec *iov)
248 {
249 u64 type;
250 u16 len, dlen;
251
252 len = sizeof(*buf);
253
254 switch ((type = reparse_mode_nfs_type(mode))) {
255 case NFS_SPECFILE_BLK:
256 case NFS_SPECFILE_CHR:
257 dlen = sizeof(__le64);
258 break;
259 case NFS_SPECFILE_FIFO:
260 case NFS_SPECFILE_SOCK:
261 dlen = 0;
262 break;
263 default:
264 return -EOPNOTSUPP;
265 }
266
267 buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
268 buf->Reserved = 0;
269 buf->InodeType = cpu_to_le64(type);
270 buf->ReparseDataLength = cpu_to_le16(len + dlen -
271 sizeof(struct reparse_data_buffer));
272 *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |
273 MAJOR(dev));
274 iov->iov_base = buf;
275 iov->iov_len = len + dlen;
276 return 0;
277 }
278
mknod_nfs(unsigned int xid,struct inode * inode,struct dentry * dentry,struct cifs_tcon * tcon,const char * full_path,umode_t mode,dev_t dev)279 static int mknod_nfs(unsigned int xid, struct inode *inode,
280 struct dentry *dentry, struct cifs_tcon *tcon,
281 const char *full_path, umode_t mode, dev_t dev)
282 {
283 struct cifs_open_info_data data;
284 struct reparse_posix_data *p;
285 struct inode *new;
286 struct kvec iov;
287 __u8 buf[sizeof(*p) + sizeof(__le64)];
288 int rc;
289
290 p = (struct reparse_posix_data *)buf;
291 rc = nfs_set_reparse_buf(p, mode, dev, &iov);
292 if (rc)
293 return rc;
294
295 data = (struct cifs_open_info_data) {
296 .reparse_point = true,
297 .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
298 };
299
300 new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
301 tcon, full_path, false, &iov, NULL);
302 if (!IS_ERR(new))
303 d_instantiate(dentry, new);
304 else
305 rc = PTR_ERR(new);
306 cifs_free_open_info(&data);
307 return rc;
308 }
309
wsl_set_reparse_buf(struct reparse_data_buffer * buf,mode_t mode,struct kvec * iov)310 static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
311 mode_t mode, struct kvec *iov)
312 {
313 u32 tag;
314
315 switch ((tag = reparse_mode_wsl_tag(mode))) {
316 case IO_REPARSE_TAG_LX_BLK:
317 case IO_REPARSE_TAG_LX_CHR:
318 case IO_REPARSE_TAG_LX_FIFO:
319 case IO_REPARSE_TAG_AF_UNIX:
320 break;
321 default:
322 return -EOPNOTSUPP;
323 }
324
325 buf->ReparseTag = cpu_to_le32(tag);
326 buf->Reserved = 0;
327 buf->ReparseDataLength = 0;
328 iov->iov_base = buf;
329 iov->iov_len = sizeof(*buf);
330 return 0;
331 }
332
ea_create_context(u32 dlen,size_t * cc_len)333 static struct smb2_create_ea_ctx *ea_create_context(u32 dlen, size_t *cc_len)
334 {
335 struct smb2_create_ea_ctx *cc;
336
337 *cc_len = round_up(sizeof(*cc) + dlen, 8);
338 cc = kzalloc(*cc_len, GFP_KERNEL);
339 if (!cc)
340 return ERR_PTR(-ENOMEM);
341
342 cc->ctx.NameOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx,
343 name));
344 cc->ctx.NameLength = cpu_to_le16(4);
345 memcpy(cc->name, SMB2_CREATE_EA_BUFFER, strlen(SMB2_CREATE_EA_BUFFER));
346 cc->ctx.DataOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ea));
347 cc->ctx.DataLength = cpu_to_le32(dlen);
348 return cc;
349 }
350
351 struct wsl_xattr {
352 const char *name;
353 __le64 value;
354 u16 size;
355 u32 next;
356 };
357
wsl_set_xattrs(struct inode * inode,umode_t _mode,dev_t _dev,struct kvec * iov)358 static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
359 dev_t _dev, struct kvec *iov)
360 {
361 struct smb2_file_full_ea_info *ea;
362 struct smb2_create_ea_ctx *cc;
363 struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
364 __le64 uid = cpu_to_le64(from_kuid(current_user_ns(), ctx->linux_uid));
365 __le64 gid = cpu_to_le64(from_kgid(current_user_ns(), ctx->linux_gid));
366 __le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
367 __le64 mode = cpu_to_le64(_mode);
368 struct wsl_xattr xattrs[] = {
369 { .name = SMB2_WSL_XATTR_UID, .value = uid, .size = SMB2_WSL_XATTR_UID_SIZE, },
370 { .name = SMB2_WSL_XATTR_GID, .value = gid, .size = SMB2_WSL_XATTR_GID_SIZE, },
371 { .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
372 { .name = SMB2_WSL_XATTR_DEV, .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
373 };
374 size_t cc_len;
375 u32 dlen = 0, next = 0;
376 int i, num_xattrs;
377 u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;
378
379 memset(iov, 0, sizeof(*iov));
380
381 /* Exclude $LXDEV xattr for sockets and fifos */
382 if (S_ISSOCK(_mode) || S_ISFIFO(_mode))
383 num_xattrs = ARRAY_SIZE(xattrs) - 1;
384 else
385 num_xattrs = ARRAY_SIZE(xattrs);
386
387 for (i = 0; i < num_xattrs; i++) {
388 xattrs[i].next = ALIGN(sizeof(*ea) + name_size +
389 xattrs[i].size, 4);
390 dlen += xattrs[i].next;
391 }
392
393 cc = ea_create_context(dlen, &cc_len);
394 if (IS_ERR(cc))
395 return PTR_ERR(cc);
396
397 ea = &cc->ea;
398 for (i = 0; i < num_xattrs; i++) {
399 ea = (void *)((u8 *)ea + next);
400 next = xattrs[i].next;
401 ea->next_entry_offset = cpu_to_le32(next);
402
403 ea->ea_name_length = name_size - 1;
404 ea->ea_value_length = cpu_to_le16(xattrs[i].size);
405 memcpy(ea->ea_data, xattrs[i].name, name_size);
406 memcpy(&ea->ea_data[name_size],
407 &xattrs[i].value, xattrs[i].size);
408 }
409 ea->next_entry_offset = 0;
410
411 iov->iov_base = cc;
412 iov->iov_len = cc_len;
413 return 0;
414 }
415
mknod_wsl(unsigned int xid,struct inode * inode,struct dentry * dentry,struct cifs_tcon * tcon,const char * full_path,umode_t mode,dev_t dev)416 static int mknod_wsl(unsigned int xid, struct inode *inode,
417 struct dentry *dentry, struct cifs_tcon *tcon,
418 const char *full_path, umode_t mode, dev_t dev)
419 {
420 struct cifs_open_info_data data;
421 struct reparse_data_buffer buf;
422 struct smb2_create_ea_ctx *cc;
423 struct inode *new;
424 unsigned int len;
425 struct kvec reparse_iov, xattr_iov;
426 int rc;
427
428 rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
429 if (rc)
430 return rc;
431
432 rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
433 if (rc)
434 return rc;
435
436 data = (struct cifs_open_info_data) {
437 .reparse_point = true,
438 .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
439 };
440
441 cc = xattr_iov.iov_base;
442 len = le32_to_cpu(cc->ctx.DataLength);
443 memcpy(data.wsl.eas, &cc->ea, len);
444 data.wsl.eas_len = len;
445
446 new = smb2_get_reparse_inode(&data, inode->i_sb,
447 xid, tcon, full_path, false,
448 &reparse_iov, &xattr_iov);
449 if (!IS_ERR(new))
450 d_instantiate(dentry, new);
451 else
452 rc = PTR_ERR(new);
453 cifs_free_open_info(&data);
454 kfree(xattr_iov.iov_base);
455 return rc;
456 }
457
smb2_mknod_reparse(unsigned int xid,struct inode * inode,struct dentry * dentry,struct cifs_tcon * tcon,const char * full_path,umode_t mode,dev_t dev)458 int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
459 struct dentry *dentry, struct cifs_tcon *tcon,
460 const char *full_path, umode_t mode, dev_t dev)
461 {
462 struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
463 int rc = -EOPNOTSUPP;
464
465 switch (ctx->reparse_type) {
466 case CIFS_REPARSE_TYPE_NFS:
467 rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
468 break;
469 case CIFS_REPARSE_TYPE_WSL:
470 rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
471 break;
472 }
473 return rc;
474 }
475
476 /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
parse_reparse_posix(struct reparse_posix_data * buf,struct cifs_sb_info * cifs_sb,struct cifs_open_info_data * data)477 static int parse_reparse_posix(struct reparse_posix_data *buf,
478 struct cifs_sb_info *cifs_sb,
479 struct cifs_open_info_data *data)
480 {
481 unsigned int len;
482 u64 type;
483
484 len = le16_to_cpu(buf->ReparseDataLength);
485 if (len < sizeof(buf->InodeType)) {
486 cifs_dbg(VFS, "srv returned malformed nfs buffer\n");
487 return -EIO;
488 }
489
490 len -= sizeof(buf->InodeType);
491
492 switch ((type = le64_to_cpu(buf->InodeType))) {
493 case NFS_SPECFILE_LNK:
494 if (len == 0 || (len % 2)) {
495 cifs_dbg(VFS, "srv returned malformed nfs symlink buffer\n");
496 return -EIO;
497 }
498 /*
499 * Check that buffer does not contain UTF-16 null codepoint
500 * because Linux cannot process symlink with null byte.
501 */
502 if (UniStrnlen((wchar_t *)buf->DataBuffer, len/2) != len/2) {
503 cifs_dbg(VFS, "srv returned null byte in nfs symlink target location\n");
504 return -EIO;
505 }
506 data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
507 len, true,
508 cifs_sb->local_nls);
509 if (!data->symlink_target)
510 return -ENOMEM;
511 cifs_dbg(FYI, "%s: target path: %s\n",
512 __func__, data->symlink_target);
513 break;
514 case NFS_SPECFILE_CHR:
515 case NFS_SPECFILE_BLK:
516 /* DataBuffer for block and char devices contains two 32-bit numbers */
517 if (len != 8) {
518 cifs_dbg(VFS, "srv returned malformed nfs buffer for type: 0x%llx\n", type);
519 return -EIO;
520 }
521 break;
522 case NFS_SPECFILE_FIFO:
523 case NFS_SPECFILE_SOCK:
524 /* DataBuffer for fifos and sockets is empty */
525 if (len != 0) {
526 cifs_dbg(VFS, "srv returned malformed nfs buffer for type: 0x%llx\n", type);
527 return -EIO;
528 }
529 break;
530 default:
531 cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
532 __func__, type);
533 return -EOPNOTSUPP;
534 }
535 return 0;
536 }
537
smb2_parse_native_symlink(char ** target,const char * buf,unsigned int len,bool unicode,bool relative,const char * full_path,struct cifs_sb_info * cifs_sb)538 int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
539 bool unicode, bool relative,
540 const char *full_path,
541 struct cifs_sb_info *cifs_sb)
542 {
543 char sep = CIFS_DIR_SEP(cifs_sb);
544 char *linux_target = NULL;
545 char *smb_target = NULL;
546 int levels;
547 int rc;
548 int i;
549
550 smb_target = cifs_strndup_from_utf16(buf, len, unicode, cifs_sb->local_nls);
551 if (!smb_target) {
552 rc = -ENOMEM;
553 goto out;
554 }
555
556 if (smb_target[0] == sep && relative) {
557 /*
558 * This is a relative SMB symlink from the top of the share,
559 * which is the top level directory of the Linux mount point.
560 * Linux does not support such relative symlinks, so convert
561 * it to the relative symlink from the current directory.
562 * full_path is the SMB path to the symlink (from which is
563 * extracted current directory) and smb_target is the SMB path
564 * where symlink points, therefore full_path must always be on
565 * the SMB share.
566 */
567 int smb_target_len = strlen(smb_target)+1;
568 levels = 0;
569 for (i = 1; full_path[i]; i++) { /* i=1 to skip leading sep */
570 if (full_path[i] == sep)
571 levels++;
572 }
573 linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL);
574 if (!linux_target) {
575 rc = -ENOMEM;
576 goto out;
577 }
578 for (i = 0; i < levels; i++) {
579 linux_target[i*3 + 0] = '.';
580 linux_target[i*3 + 1] = '.';
581 linux_target[i*3 + 2] = sep;
582 }
583 memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */
584 } else {
585 linux_target = smb_target;
586 smb_target = NULL;
587 }
588
589 if (sep == '\\')
590 convert_delimiter(linux_target, '/');
591
592 rc = 0;
593 *target = linux_target;
594
595 cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, *target);
596
597 out:
598 if (rc != 0)
599 kfree(linux_target);
600 kfree(smb_target);
601 return rc;
602 }
603
parse_reparse_symlink(struct reparse_symlink_data_buffer * sym,u32 plen,bool unicode,struct cifs_sb_info * cifs_sb,const char * full_path,struct cifs_open_info_data * data)604 static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
605 u32 plen, bool unicode,
606 struct cifs_sb_info *cifs_sb,
607 const char *full_path,
608 struct cifs_open_info_data *data)
609 {
610 unsigned int len;
611 unsigned int offs;
612
613 /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
614
615 offs = le16_to_cpu(sym->SubstituteNameOffset);
616 len = le16_to_cpu(sym->SubstituteNameLength);
617 if (offs + 20 > plen || offs + len + 20 > plen) {
618 cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
619 return -EIO;
620 }
621
622 return smb2_parse_native_symlink(&data->symlink_target,
623 sym->PathBuffer + offs,
624 len,
625 unicode,
626 le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE,
627 full_path,
628 cifs_sb);
629 }
630
parse_reparse_point(struct reparse_data_buffer * buf,u32 plen,struct cifs_sb_info * cifs_sb,const char * full_path,bool unicode,struct cifs_open_info_data * data)631 int parse_reparse_point(struct reparse_data_buffer *buf,
632 u32 plen, struct cifs_sb_info *cifs_sb,
633 const char *full_path,
634 bool unicode, struct cifs_open_info_data *data)
635 {
636 struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
637
638 data->reparse.buf = buf;
639
640 /* See MS-FSCC 2.1.2 */
641 switch (le32_to_cpu(buf->ReparseTag)) {
642 case IO_REPARSE_TAG_NFS:
643 return parse_reparse_posix((struct reparse_posix_data *)buf,
644 cifs_sb, data);
645 case IO_REPARSE_TAG_SYMLINK:
646 return parse_reparse_symlink(
647 (struct reparse_symlink_data_buffer *)buf,
648 plen, unicode, cifs_sb, full_path, data);
649 case IO_REPARSE_TAG_LX_SYMLINK:
650 case IO_REPARSE_TAG_AF_UNIX:
651 case IO_REPARSE_TAG_LX_FIFO:
652 case IO_REPARSE_TAG_LX_CHR:
653 case IO_REPARSE_TAG_LX_BLK:
654 break;
655 default:
656 cifs_tcon_dbg(VFS | ONCE, "unhandled reparse tag: 0x%08x\n",
657 le32_to_cpu(buf->ReparseTag));
658 break;
659 }
660 return 0;
661 }
662
smb2_parse_reparse_point(struct cifs_sb_info * cifs_sb,const char * full_path,struct kvec * rsp_iov,struct cifs_open_info_data * data)663 int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
664 const char *full_path,
665 struct kvec *rsp_iov,
666 struct cifs_open_info_data *data)
667 {
668 struct reparse_data_buffer *buf;
669 struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
670 u32 plen = le32_to_cpu(io->OutputCount);
671
672 buf = (struct reparse_data_buffer *)((u8 *)io +
673 le32_to_cpu(io->OutputOffset));
674 return parse_reparse_point(buf, plen, cifs_sb, full_path, true, data);
675 }
676
wsl_to_fattr(struct cifs_open_info_data * data,struct cifs_sb_info * cifs_sb,u32 tag,struct cifs_fattr * fattr)677 static void wsl_to_fattr(struct cifs_open_info_data *data,
678 struct cifs_sb_info *cifs_sb,
679 u32 tag, struct cifs_fattr *fattr)
680 {
681 struct smb2_file_full_ea_info *ea;
682 u32 next = 0;
683
684 switch (tag) {
685 case IO_REPARSE_TAG_LX_SYMLINK:
686 fattr->cf_mode |= S_IFLNK;
687 break;
688 case IO_REPARSE_TAG_LX_FIFO:
689 fattr->cf_mode |= S_IFIFO;
690 break;
691 case IO_REPARSE_TAG_AF_UNIX:
692 fattr->cf_mode |= S_IFSOCK;
693 break;
694 case IO_REPARSE_TAG_LX_CHR:
695 fattr->cf_mode |= S_IFCHR;
696 break;
697 case IO_REPARSE_TAG_LX_BLK:
698 fattr->cf_mode |= S_IFBLK;
699 break;
700 }
701
702 if (!data->wsl.eas_len)
703 goto out;
704
705 ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
706 do {
707 const char *name;
708 void *v;
709 u8 nlen;
710
711 ea = (void *)((u8 *)ea + next);
712 next = le32_to_cpu(ea->next_entry_offset);
713 if (!le16_to_cpu(ea->ea_value_length))
714 continue;
715
716 name = ea->ea_data;
717 nlen = ea->ea_name_length;
718 v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1);
719
720 if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen))
721 fattr->cf_uid = wsl_make_kuid(cifs_sb, v);
722 else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))
723 fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
724 else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
725 fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
726 else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
727 fattr->cf_rdev = reparse_mkdev(v);
728 } while (next);
729 out:
730 fattr->cf_dtype = S_DT(fattr->cf_mode);
731 }
732
posix_reparse_to_fattr(struct cifs_sb_info * cifs_sb,struct cifs_fattr * fattr,struct cifs_open_info_data * data)733 static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
734 struct cifs_fattr *fattr,
735 struct cifs_open_info_data *data)
736 {
737 struct reparse_posix_data *buf = data->reparse.posix;
738
739
740 if (buf == NULL)
741 return true;
742
743 if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType)) {
744 WARN_ON_ONCE(1);
745 return false;
746 }
747
748 switch (le64_to_cpu(buf->InodeType)) {
749 case NFS_SPECFILE_CHR:
750 if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
751 WARN_ON_ONCE(1);
752 return false;
753 }
754 fattr->cf_mode |= S_IFCHR;
755 fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
756 break;
757 case NFS_SPECFILE_BLK:
758 if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
759 WARN_ON_ONCE(1);
760 return false;
761 }
762 fattr->cf_mode |= S_IFBLK;
763 fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
764 break;
765 case NFS_SPECFILE_FIFO:
766 fattr->cf_mode |= S_IFIFO;
767 break;
768 case NFS_SPECFILE_SOCK:
769 fattr->cf_mode |= S_IFSOCK;
770 break;
771 case NFS_SPECFILE_LNK:
772 fattr->cf_mode |= S_IFLNK;
773 break;
774 default:
775 WARN_ON_ONCE(1);
776 return false;
777 }
778 return true;
779 }
780
cifs_reparse_point_to_fattr(struct cifs_sb_info * cifs_sb,struct cifs_fattr * fattr,struct cifs_open_info_data * data)781 bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
782 struct cifs_fattr *fattr,
783 struct cifs_open_info_data *data)
784 {
785 u32 tag = data->reparse.tag;
786 bool ok;
787
788 switch (tag) {
789 case IO_REPARSE_TAG_INTERNAL:
790 if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY))
791 return false;
792 fallthrough;
793 case IO_REPARSE_TAG_DFS:
794 case IO_REPARSE_TAG_DFSR:
795 case IO_REPARSE_TAG_MOUNT_POINT:
796 /* See cifs_create_junction_fattr() */
797 fattr->cf_mode = S_IFDIR | 0711;
798 break;
799 case IO_REPARSE_TAG_LX_SYMLINK:
800 case IO_REPARSE_TAG_LX_FIFO:
801 case IO_REPARSE_TAG_AF_UNIX:
802 case IO_REPARSE_TAG_LX_CHR:
803 case IO_REPARSE_TAG_LX_BLK:
804 wsl_to_fattr(data, cifs_sb, tag, fattr);
805 break;
806 case IO_REPARSE_TAG_NFS:
807 ok = posix_reparse_to_fattr(cifs_sb, fattr, data);
808 if (!ok)
809 return false;
810 break;
811 case 0: /* SMB1 symlink */
812 case IO_REPARSE_TAG_SYMLINK:
813 fattr->cf_mode |= S_IFLNK;
814 break;
815 default:
816 return false;
817 }
818
819 fattr->cf_dtype = S_DT(fattr->cf_mode);
820 return true;
821 }
822