int parse_reparse_point(struct reparse_data_buffer *buf,
u32 plen, struct cifs_sb_info *cifs_sb,
bool unicode, struct cifs_open_info_data *data);
+int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev,
+ const char *symname);
int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);
if (ctx->mfsymlinks) {
if (ctx->sfu_emul) {
/*
- * Our SFU ("Services for Unix" emulation does not allow
- * creating symlinks but does allow reading existing SFU
- * symlinks (it does allow both creating and reading SFU
- * style mknod and FIFOs though). When "mfsymlinks" and
+ * Our SFU ("Services for Unix") emulation allows now
+ * creating new and reading existing SFU symlinks.
+ * Older Linux kernel versions were not able to neither
+ * read existing nor create new SFU symlinks. But
+ * creating and reading SFU style mknod and FIFOs was
+ * supported for long time. When "mfsymlinks" and
* "sfu" are both enabled at the same time, it allows
* reading both types of symlinks, but will only create
* them with mfsymlinks format. This allows better
- * Apple compatibility (probably better for Samba too)
+ * Apple compatibility, compatibility with older Linux
+ * kernel clients (probably better for Samba too)
* while still recognizing old Windows style symlinks.
*/
cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
/* BB what if DFS and this volume is on different share? BB */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
+ full_path, S_IFLNK, 0, symname);
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
} else if (pTcon->unix_ext) {
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
return 0;
}
-static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
+int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
- const char *full_path, umode_t mode, dev_t dev)
+ const char *full_path, umode_t mode, dev_t dev,
+ const char *symname)
{
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_fid fid;
unsigned int bytes_written;
- struct win_dev pdev = {};
- struct kvec iov[2];
+ u8 type[8];
+ int type_len = 0;
+ struct {
+ __le64 major;
+ __le64 minor;
+ } __packed pdev = {};
+ __le16 *symname_utf16 = NULL;
+ u8 *data = NULL;
+ int data_len = 0;
+ struct kvec iov[3];
__u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
int rc;
switch (mode & S_IFMT) {
case S_IFCHR:
- memcpy(pdev.type, "IntxCHR\0", 8);
+ type_len = 8;
+ memcpy(type, "IntxCHR\0", type_len);
pdev.major = cpu_to_le64(MAJOR(dev));
pdev.minor = cpu_to_le64(MINOR(dev));
+ data = (u8 *)&pdev;
+ data_len = sizeof(pdev);
break;
case S_IFBLK:
- memcpy(pdev.type, "IntxBLK\0", 8);
+ type_len = 8;
+ memcpy(type, "IntxBLK\0", type_len);
pdev.major = cpu_to_le64(MAJOR(dev));
pdev.minor = cpu_to_le64(MINOR(dev));
+ data = (u8 *)&pdev;
+ data_len = sizeof(pdev);
+ break;
+ case S_IFLNK:
+ type_len = 8;
+ memcpy(type, "IntxLNK\1", type_len);
+ symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
+ &data_len, cifs_sb->local_nls,
+ NO_MAP_UNI_RSVD);
+ if (!symname_utf16) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ data_len -= 2; /* symlink is without trailing wide-nul */
+ data = (u8 *)symname_utf16;
break;
case S_IFSOCK:
- strscpy(pdev.type, "LnxSOCK");
+ type_len = 8;
+ strscpy(type, "LnxSOCK");
+ data = (u8 *)&pdev;
+ data_len = sizeof(pdev);
break;
case S_IFIFO:
- strscpy(pdev.type, "LnxFIFO");
+ type_len = 8;
+ strscpy(type, "LnxFIFO");
+ data = (u8 *)&pdev;
+ data_len = sizeof(pdev);
break;
default:
- return -EPERM;
+ rc = -EPERM;
+ goto out;
}
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE,
rc = server->ops->open(xid, &oparms, &oplock, NULL);
if (rc)
- return rc;
+ goto out;
- io_parms.pid = current->tgid;
- io_parms.tcon = tcon;
- io_parms.length = sizeof(pdev);
- iov[1].iov_base = &pdev;
- iov[1].iov_len = sizeof(pdev);
+ if (type_len + data_len > 0) {
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.length = type_len + data_len;
+ iov[1].iov_base = type;
+ iov[1].iov_len = type_len;
+ iov[2].iov_base = data;
+ iov[2].iov_len = data_len;
+
+ rc = server->ops->sync_write(xid, &fid, &io_parms,
+ &bytes_written,
+ iov, ARRAY_SIZE(iov)-1);
+ }
- rc = server->ops->sync_write(xid, &fid, &io_parms,
- &bytes_written, iov, 1);
server->ops->close(xid, tcon, &fid);
+
+out:
+ kfree(symname_utf16);
return rc;
}
int rc;
rc = __cifs_sfu_make_node(xid, inode, dentry, tcon,
- full_path, mode, dev);
+ full_path, mode, dev, NULL);
if (rc)
return rc;