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>
This commit is contained in:
Pali Rohár 2024-09-12 14:05:43 +02:00 committed by Steve French
parent 89c601ab7c
commit cf2ce67345

View File

@ -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;