Skip to content

Commit 41d3f25

Browse files
palismfrench
authored andcommitted
cifs: Add support for creating SFU symlinks
Linux cifs client can already detect SFU symlinks and reads it content (target location). But currently is not able to create new symlink. So implement this missing support. When 'sfu' mount option is specified and 'mfsymlinks' is not specified then create new symlinks in SFU-style. This will provide full SFU compatibility of symlinks when mounting cifs share with 'sfu' option. 'mfsymlinks' option override SFU for better Apple compatibility as explained in fs_context.c file in smb3_update_mnt_flags() function. Extend __cifs_sfu_make_node() function, which now can handle also S_IFLNK type and refactor structures passed to sync_write() in this function, by splitting SFU type and SFU data from original combined struct win_dev as combined fixed-length struct cannot be used for variable-length symlinks. Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 21dcbc1 commit 41d3f25

File tree

5 files changed

+77
-29
lines changed

5 files changed

+77
-29
lines changed

fs/smb/client/cifspdu.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,12 +2573,6 @@ typedef struct {
25732573
} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
25742574

25752575

2576-
struct win_dev {
2577-
unsigned char type[8]; /* IntxCHR or IntxBLK or LnxFIFO or LnxSOCK */
2578-
__le64 major;
2579-
__le64 minor;
2580-
} __attribute__((packed));
2581-
25822576
struct fea {
25832577
unsigned char EA_flags;
25842578
__u8 name_len;

fs/smb/client/cifsproto.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,10 @@ char *extract_sharename(const char *unc);
676676
int parse_reparse_point(struct reparse_data_buffer *buf,
677677
u32 plen, struct cifs_sb_info *cifs_sb,
678678
bool unicode, struct cifs_open_info_data *data);
679+
int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
680+
struct dentry *dentry, struct cifs_tcon *tcon,
681+
const char *full_path, umode_t mode, dev_t dev,
682+
const char *symname);
679683
int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
680684
struct dentry *dentry, struct cifs_tcon *tcon,
681685
const char *full_path, umode_t mode, dev_t dev);

fs/smb/client/fs_context.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,14 +1899,17 @@ void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb)
18991899
if (ctx->mfsymlinks) {
19001900
if (ctx->sfu_emul) {
19011901
/*
1902-
* Our SFU ("Services for Unix" emulation does not allow
1903-
* creating symlinks but does allow reading existing SFU
1904-
* symlinks (it does allow both creating and reading SFU
1905-
* style mknod and FIFOs though). When "mfsymlinks" and
1902+
* Our SFU ("Services for Unix") emulation allows now
1903+
* creating new and reading existing SFU symlinks.
1904+
* Older Linux kernel versions were not able to neither
1905+
* read existing nor create new SFU symlinks. But
1906+
* creating and reading SFU style mknod and FIFOs was
1907+
* supported for long time. When "mfsymlinks" and
19061908
* "sfu" are both enabled at the same time, it allows
19071909
* reading both types of symlinks, but will only create
19081910
* them with mfsymlinks format. This allows better
1909-
* Apple compatibility (probably better for Samba too)
1911+
* Apple compatibility, compatibility with older Linux
1912+
* kernel clients (probably better for Samba too)
19101913
* while still recognizing old Windows style symlinks.
19111914
*/
19121915
cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");

fs/smb/client/link.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,9 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
606606
/* BB what if DFS and this volume is on different share? BB */
607607
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
608608
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
609+
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
610+
rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
611+
full_path, S_IFLNK, 0, symname);
609612
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
610613
} else if (pTcon->unix_ext) {
611614
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,

fs/smb/client/smb2ops.c

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5055,40 +5055,75 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
50555055
return 0;
50565056
}
50575057

5058-
static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
5058+
int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
50595059
struct dentry *dentry, struct cifs_tcon *tcon,
5060-
const char *full_path, umode_t mode, dev_t dev)
5060+
const char *full_path, umode_t mode, dev_t dev,
5061+
const char *symname)
50615062
{
50625063
struct TCP_Server_Info *server = tcon->ses->server;
50635064
struct cifs_open_parms oparms;
50645065
struct cifs_io_parms io_parms = {};
50655066
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
50665067
struct cifs_fid fid;
50675068
unsigned int bytes_written;
5068-
struct win_dev pdev = {};
5069-
struct kvec iov[2];
5069+
u8 type[8];
5070+
int type_len = 0;
5071+
struct {
5072+
__le64 major;
5073+
__le64 minor;
5074+
} __packed pdev = {};
5075+
__le16 *symname_utf16 = NULL;
5076+
u8 *data = NULL;
5077+
int data_len = 0;
5078+
struct kvec iov[3];
50705079
__u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
50715080
int rc;
50725081

50735082
switch (mode & S_IFMT) {
50745083
case S_IFCHR:
5075-
memcpy(pdev.type, "IntxCHR\0", 8);
5084+
type_len = 8;
5085+
memcpy(type, "IntxCHR\0", type_len);
50765086
pdev.major = cpu_to_le64(MAJOR(dev));
50775087
pdev.minor = cpu_to_le64(MINOR(dev));
5088+
data = (u8 *)&pdev;
5089+
data_len = sizeof(pdev);
50785090
break;
50795091
case S_IFBLK:
5080-
memcpy(pdev.type, "IntxBLK\0", 8);
5092+
type_len = 8;
5093+
memcpy(type, "IntxBLK\0", type_len);
50815094
pdev.major = cpu_to_le64(MAJOR(dev));
50825095
pdev.minor = cpu_to_le64(MINOR(dev));
5096+
data = (u8 *)&pdev;
5097+
data_len = sizeof(pdev);
5098+
break;
5099+
case S_IFLNK:
5100+
type_len = 8;
5101+
memcpy(type, "IntxLNK\1", type_len);
5102+
symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
5103+
&data_len, cifs_sb->local_nls,
5104+
NO_MAP_UNI_RSVD);
5105+
if (!symname_utf16) {
5106+
rc = -ENOMEM;
5107+
goto out;
5108+
}
5109+
data_len -= 2; /* symlink is without trailing wide-nul */
5110+
data = (u8 *)symname_utf16;
50835111
break;
50845112
case S_IFSOCK:
5085-
strscpy(pdev.type, "LnxSOCK");
5113+
type_len = 8;
5114+
strscpy(type, "LnxSOCK");
5115+
data = (u8 *)&pdev;
5116+
data_len = sizeof(pdev);
50865117
break;
50875118
case S_IFIFO:
5088-
strscpy(pdev.type, "LnxFIFO");
5119+
type_len = 8;
5120+
strscpy(type, "LnxFIFO");
5121+
data = (u8 *)&pdev;
5122+
data_len = sizeof(pdev);
50895123
break;
50905124
default:
5091-
return -EPERM;
5125+
rc = -EPERM;
5126+
goto out;
50925127
}
50935128

50945129
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE,
@@ -5098,17 +5133,26 @@ static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
50985133

50995134
rc = server->ops->open(xid, &oparms, &oplock, NULL);
51005135
if (rc)
5101-
return rc;
5136+
goto out;
51025137

5103-
io_parms.pid = current->tgid;
5104-
io_parms.tcon = tcon;
5105-
io_parms.length = sizeof(pdev);
5106-
iov[1].iov_base = &pdev;
5107-
iov[1].iov_len = sizeof(pdev);
5138+
if (type_len + data_len > 0) {
5139+
io_parms.pid = current->tgid;
5140+
io_parms.tcon = tcon;
5141+
io_parms.length = type_len + data_len;
5142+
iov[1].iov_base = type;
5143+
iov[1].iov_len = type_len;
5144+
iov[2].iov_base = data;
5145+
iov[2].iov_len = data_len;
5146+
5147+
rc = server->ops->sync_write(xid, &fid, &io_parms,
5148+
&bytes_written,
5149+
iov, ARRAY_SIZE(iov)-1);
5150+
}
51085151

5109-
rc = server->ops->sync_write(xid, &fid, &io_parms,
5110-
&bytes_written, iov, 1);
51115152
server->ops->close(xid, tcon, &fid);
5153+
5154+
out:
5155+
kfree(symname_utf16);
51125156
return rc;
51135157
}
51145158

@@ -5120,7 +5164,7 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
51205164
int rc;
51215165

51225166
rc = __cifs_sfu_make_node(xid, inode, dentry, tcon,
5123-
full_path, mode, dev);
5167+
full_path, mode, dev, NULL);
51245168
if (rc)
51255169
return rc;
51265170

0 commit comments

Comments
 (0)