summaryrefslogtreecommitdiff
path: root/fs/smb
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2024-09-12 15:05:43 +0300
committerSteve French <stfrench@microsoft.com>2024-09-15 18:42:45 +0300
commitcf2ce67345d6a1af0853d8a7aef9ab8e6ea597d5 (patch)
treec0f74f31f94349b8cf627583335d345acb823c43 /fs/smb
parent89c601ab7cb3f520d59a653ddde2dfddd50986fb (diff)
downloadlinux-cf2ce67345d6a1af0853d8a7aef9ab8e6ea597d5.tar.xz
cifs: Add support for reading SFU symlink location
Currently when sfu mount option is specified then CIFS can recognize SFU symlink, but is not able to read symlink target location. readlink() syscall just returns that operation is not supported. Implement this missing functionality in cifs_sfu_type() function. Read target location of SFU-style symlink, parse it and fill into fattr's cf_symlink_target member. SFU-style symlink is file which has system attribute set and file content is buffer "IntxLNK\1" (8th byte is 0x01) followed by the target location encoded in little endian UCS-2/UTF-16. This format was introduced in Interix 3.0 subsystem, as part of the Microsoft SFU 3.0 and is used also by all later versions. Previous versions had no symlink support. Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/smb')
-rw-r--r--fs/smb/client/inode.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 7d424e769a56..e8567ed63f22 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -529,6 +529,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
struct cifs_fid fid;
struct cifs_open_parms oparms;
struct cifs_io_parms io_parms = {0};
+ char *symlink_buf_utf16;
+ unsigned int symlink_len_utf16;
char buf[24];
unsigned int bytes_read;
char *pbuf;
@@ -616,6 +618,33 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
cifs_dbg(FYI, "Symlink\n");
fattr->cf_mode |= S_IFLNK;
fattr->cf_dtype = DT_LNK;
+ if ((fattr->cf_eof > 8) && (fattr->cf_eof % 2 == 0)) {
+ symlink_buf_utf16 = kmalloc(fattr->cf_eof-8 + 1, GFP_KERNEL);
+ if (symlink_buf_utf16) {
+ io_parms.offset = 8;
+ io_parms.length = fattr->cf_eof-8 + 1;
+ buf_type = CIFS_NO_BUFFER;
+ rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms,
+ &symlink_len_utf16,
+ &symlink_buf_utf16,
+ &buf_type);
+ if ((rc == 0) &&
+ (symlink_len_utf16 > 0) &&
+ (symlink_len_utf16 < fattr->cf_eof-8 + 1) &&
+ (symlink_len_utf16 % 2 == 0)) {
+ fattr->cf_symlink_target =
+ cifs_strndup_from_utf16(symlink_buf_utf16,
+ symlink_len_utf16,
+ true,
+ cifs_sb->local_nls);
+ if (!fattr->cf_symlink_target)
+ rc = -ENOMEM;
+ }
+ kfree(symlink_buf_utf16);
+ } else {
+ rc = -ENOMEM;
+ }
+ }
} else if (memcmp("LnxFIFO", pbuf, 8) == 0) {
cifs_dbg(FYI, "FIFO\n");
fattr->cf_mode |= S_IFIFO;