Skip to content

Commit 071b8a6

Browse files
paliSteve French
authored andcommitted
cifs: Add support for creating NFS-style symlinks
CIFS client is currently able to parse NFS-style symlinks, but is not able to create them. This functionality is useful when the mounted SMB share is used also by Windows NFS server (on Windows Server 2012 or new). It allows interop of symlinks between SMB share mounted by Linux CIFS client and same export from Windows NFS server mounted by some NFS client. New symlinks would be created in NFS-style only in case the mount option -o reparse=nfs is specified, which is not by default. So default CIFS mounts are not affected by this change. Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 45a99d5 commit 071b8a6

File tree

1 file changed

+39
-8
lines changed

1 file changed

+39
-8
lines changed

fs/smb/client/reparse.c

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ static int create_native_socket(const unsigned int xid, struct inode *inode,
409409

410410
static int nfs_set_reparse_buf(struct reparse_nfs_data_buffer *buf,
411411
mode_t mode, dev_t dev,
412+
__le16 *symname_utf16,
413+
int symname_utf16_len,
412414
struct kvec *iov)
413415
{
414416
u64 type;
@@ -419,13 +421,18 @@ static int nfs_set_reparse_buf(struct reparse_nfs_data_buffer *buf,
419421
switch ((type = reparse_mode_nfs_type(mode))) {
420422
case NFS_SPECFILE_BLK:
421423
case NFS_SPECFILE_CHR:
422-
dlen = sizeof(__le64);
424+
dlen = 2 * sizeof(__le32);
425+
((__le32 *)buf->DataBuffer)[0] = cpu_to_le32(MAJOR(dev));
426+
((__le32 *)buf->DataBuffer)[1] = cpu_to_le32(MINOR(dev));
427+
break;
428+
case NFS_SPECFILE_LNK:
429+
dlen = symname_utf16_len;
430+
memcpy(buf->DataBuffer, symname_utf16, symname_utf16_len);
423431
break;
424432
case NFS_SPECFILE_FIFO:
425433
case NFS_SPECFILE_SOCK:
426434
dlen = 0;
427435
break;
428-
case NFS_SPECFILE_LNK: /* TODO: add support for NFS symlinks */
429436
default:
430437
return -EOPNOTSUPP;
431438
}
@@ -435,8 +442,6 @@ static int nfs_set_reparse_buf(struct reparse_nfs_data_buffer *buf,
435442
buf->InodeType = cpu_to_le64(type);
436443
buf->ReparseDataLength = cpu_to_le16(len + dlen -
437444
sizeof(struct reparse_data_buffer));
438-
*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |
439-
MAJOR(dev));
440445
iov->iov_base = buf;
441446
iov->iov_len = len + dlen;
442447
return 0;
@@ -447,21 +452,42 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
447452
const char *full_path, umode_t mode, dev_t dev,
448453
const char *symname)
449454
{
455+
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
450456
struct cifs_open_info_data data;
451-
struct reparse_nfs_data_buffer *p;
457+
struct reparse_nfs_data_buffer *p = NULL;
458+
__le16 *symname_utf16 = NULL;
459+
int symname_utf16_len = 0;
452460
struct inode *new;
453461
struct kvec iov;
454462
__u8 buf[sizeof(*p) + sizeof(__le64)];
455463
int rc;
456464

457-
p = (struct reparse_nfs_data_buffer *)buf;
458-
rc = nfs_set_reparse_buf(p, mode, dev, &iov);
465+
if (S_ISLNK(mode)) {
466+
symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
467+
&symname_utf16_len,
468+
cifs_sb->local_nls,
469+
NO_MAP_UNI_RSVD);
470+
if (!symname_utf16) {
471+
rc = -ENOMEM;
472+
goto out;
473+
}
474+
symname_utf16_len -= 2; /* symlink is without trailing wide-nul */
475+
p = kzalloc(sizeof(*p) + symname_utf16_len, GFP_KERNEL);
476+
if (!p) {
477+
rc = -ENOMEM;
478+
goto out;
479+
}
480+
} else {
481+
p = (struct reparse_nfs_data_buffer *)buf;
482+
}
483+
rc = nfs_set_reparse_buf(p, mode, dev, symname_utf16, symname_utf16_len, &iov);
459484
if (rc)
460-
return rc;
485+
goto out;
461486

462487
data = (struct cifs_open_info_data) {
463488
.reparse_point = true,
464489
.reparse = { .tag = IO_REPARSE_TAG_NFS, .buf = (struct reparse_data_buffer *)p, },
490+
.symlink_target = kstrdup(symname, GFP_KERNEL),
465491
};
466492

467493
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
@@ -471,6 +497,11 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
471497
else
472498
rc = PTR_ERR(new);
473499
cifs_free_open_info(&data);
500+
out:
501+
if (S_ISLNK(mode)) {
502+
kfree(symname_utf16);
503+
kfree(p);
504+
}
474505
return rc;
475506
}
476507

0 commit comments

Comments
 (0)