Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

smb: client: move most of reparse point handling code to common file

In preparation to add support for creating special files also via WSL
reparse points in next commits.

Signed-off-by: Paulo Alcantara <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paulo Alcantara and committed by
Steve French
c520ba75 eb90e8ec

+405 -364
+1 -1
fs/smb/client/Makefile
··· 12 12 smb2ops.o smb2maperror.o smb2transport.o \ 13 13 smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ 14 14 dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o \ 15 - namespace.o 15 + namespace.o reparse.o 16 16 17 17 $(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h 18 18
-13
fs/smb/client/cifsglob.h
··· 223 223 }; 224 224 }; 225 225 226 - static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data) 227 - { 228 - struct smb2_file_all_info *fi = &data->fi; 229 - u32 attrs = le32_to_cpu(fi->Attributes); 230 - bool ret; 231 - 232 - ret = data->reparse_point || (attrs & ATTR_REPARSE); 233 - if (ret) 234 - attrs |= ATTR_REPARSE; 235 - fi->Attributes = cpu_to_le32(attrs); 236 - return ret; 237 - } 238 - 239 226 /* 240 227 ***************************************************************** 241 228 * Except the CIFS PDUs themselves all the
-4
fs/smb/client/cifsproto.h
··· 210 210 int cifs_get_inode_info(struct inode **inode, const char *full_path, 211 211 struct cifs_open_info_data *data, struct super_block *sb, int xid, 212 212 const struct cifs_fid *fid); 213 - bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, 214 - struct cifs_fattr *fattr, 215 - struct cifs_open_info_data *data); 216 - 217 213 extern int smb311_posix_get_inode_info(struct inode **inode, 218 214 const char *full_path, 219 215 struct cifs_open_info_data *data,
+1 -78
fs/smb/client/inode.c
··· 26 26 #include "fs_context.h" 27 27 #include "cifs_ioctl.h" 28 28 #include "cached_dir.h" 29 + #include "reparse.h" 29 30 30 31 static void cifs_set_ops(struct inode *inode) 31 32 { ··· 727 726 728 727 cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", 729 728 fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); 730 - } 731 - 732 - static inline dev_t nfs_mkdev(struct reparse_posix_data *buf) 733 - { 734 - u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); 735 - 736 - return MKDEV(v >> 32, v & 0xffffffff); 737 - } 738 - 739 - bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, 740 - struct cifs_fattr *fattr, 741 - struct cifs_open_info_data *data) 742 - { 743 - struct reparse_posix_data *buf = data->reparse.posix; 744 - u32 tag = data->reparse.tag; 745 - 746 - if (tag == IO_REPARSE_TAG_NFS && buf) { 747 - switch (le64_to_cpu(buf->InodeType)) { 748 - case NFS_SPECFILE_CHR: 749 - fattr->cf_mode |= S_IFCHR; 750 - fattr->cf_dtype = DT_CHR; 751 - fattr->cf_rdev = nfs_mkdev(buf); 752 - break; 753 - case NFS_SPECFILE_BLK: 754 - fattr->cf_mode |= S_IFBLK; 755 - fattr->cf_dtype = DT_BLK; 756 - fattr->cf_rdev = nfs_mkdev(buf); 757 - break; 758 - case NFS_SPECFILE_FIFO: 759 - fattr->cf_mode |= S_IFIFO; 760 - fattr->cf_dtype = DT_FIFO; 761 - break; 762 - case NFS_SPECFILE_SOCK: 763 - fattr->cf_mode |= S_IFSOCK; 764 - fattr->cf_dtype = DT_SOCK; 765 - break; 766 - case NFS_SPECFILE_LNK: 767 - fattr->cf_mode |= S_IFLNK; 768 - fattr->cf_dtype = DT_LNK; 769 - break; 770 - default: 771 - WARN_ON_ONCE(1); 772 - return false; 773 - } 774 - return true; 775 - } 776 - 777 - switch (tag) { 778 - case IO_REPARSE_TAG_LX_SYMLINK: 779 - fattr->cf_mode |= S_IFLNK; 780 - fattr->cf_dtype = DT_LNK; 781 - break; 782 - case IO_REPARSE_TAG_LX_FIFO: 783 - fattr->cf_mode |= S_IFIFO; 784 - fattr->cf_dtype = DT_FIFO; 785 - break; 786 - case IO_REPARSE_TAG_AF_UNIX: 787 - fattr->cf_mode |= S_IFSOCK; 788 - fattr->cf_dtype = DT_SOCK; 789 - break; 790 - case IO_REPARSE_TAG_LX_CHR: 791 - fattr->cf_mode |= S_IFCHR; 792 - fattr->cf_dtype = DT_CHR; 793 - break; 794 - case IO_REPARSE_TAG_LX_BLK: 795 - fattr->cf_mode |= S_IFBLK; 796 - fattr->cf_dtype = DT_BLK; 797 - break; 798 - case 0: /* SMB1 symlink */ 799 - case IO_REPARSE_TAG_SYMLINK: 800 - case IO_REPARSE_TAG_NFS: 801 - fattr->cf_mode |= S_IFLNK; 802 - fattr->cf_dtype = DT_LNK; 803 - break; 804 - default: 805 - return false; 806 - } 807 - return true; 808 729 } 809 730 810 731 static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
+1 -17
fs/smb/client/readdir.c
··· 22 22 #include "smb2proto.h" 23 23 #include "fs_context.h" 24 24 #include "cached_dir.h" 25 + #include "reparse.h" 25 26 26 27 /* 27 28 * To be safe - for UCS to UTF-8 with strings loaded with the rare long ··· 55 54 { 56 55 } 57 56 #endif /* DEBUG2 */ 58 - 59 - /* 60 - * Match a reparse point inode if reparse tag and ctime haven't changed. 61 - * 62 - * Windows Server updates ctime of reparse points when their data have changed. 63 - * The server doesn't allow changing reparse tags from existing reparse points, 64 - * though it's worth checking. 65 - */ 66 - static inline bool reparse_inode_match(struct inode *inode, 67 - struct cifs_fattr *fattr) 68 - { 69 - struct timespec64 ctime = inode_get_ctime(inode); 70 - 71 - return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) && 72 - CIFS_I(inode)->reparse_tag == fattr->cf_cifstag && 73 - timespec64_equal(&ctime, &fattr->cf_ctime); 74 - } 75 57 76 58 /* 77 59 * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
+320
fs/smb/client/reparse.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com> 4 + */ 5 + 6 + #include <linux/fs.h> 7 + #include <linux/stat.h> 8 + #include <linux/slab.h> 9 + #include "cifsglob.h" 10 + #include "smb2proto.h" 11 + #include "cifsproto.h" 12 + #include "cifs_unicode.h" 13 + #include "cifs_debug.h" 14 + #include "reparse.h" 15 + 16 + int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, 17 + struct dentry *dentry, struct cifs_tcon *tcon, 18 + const char *full_path, const char *symname) 19 + { 20 + struct reparse_symlink_data_buffer *buf = NULL; 21 + struct cifs_open_info_data data; 22 + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 23 + struct inode *new; 24 + struct kvec iov; 25 + __le16 *path; 26 + char *sym, sep = CIFS_DIR_SEP(cifs_sb); 27 + u16 len, plen; 28 + int rc = 0; 29 + 30 + sym = kstrdup(symname, GFP_KERNEL); 31 + if (!sym) 32 + return -ENOMEM; 33 + 34 + data = (struct cifs_open_info_data) { 35 + .reparse_point = true, 36 + .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, }, 37 + .symlink_target = sym, 38 + }; 39 + 40 + convert_delimiter(sym, sep); 41 + path = cifs_convert_path_to_utf16(sym, cifs_sb); 42 + if (!path) { 43 + rc = -ENOMEM; 44 + goto out; 45 + } 46 + 47 + plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); 48 + len = sizeof(*buf) + plen * 2; 49 + buf = kzalloc(len, GFP_KERNEL); 50 + if (!buf) { 51 + rc = -ENOMEM; 52 + goto out; 53 + } 54 + 55 + buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK); 56 + buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer)); 57 + buf->SubstituteNameOffset = cpu_to_le16(plen); 58 + buf->SubstituteNameLength = cpu_to_le16(plen); 59 + memcpy(&buf->PathBuffer[plen], path, plen); 60 + buf->PrintNameOffset = 0; 61 + buf->PrintNameLength = cpu_to_le16(plen); 62 + memcpy(buf->PathBuffer, path, plen); 63 + buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); 64 + if (*sym != sep) 65 + buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE); 66 + 67 + convert_delimiter(sym, '/'); 68 + iov.iov_base = buf; 69 + iov.iov_len = len; 70 + new = smb2_get_reparse_inode(&data, inode->i_sb, xid, 71 + tcon, full_path, &iov); 72 + if (!IS_ERR(new)) 73 + d_instantiate(dentry, new); 74 + else 75 + rc = PTR_ERR(new); 76 + out: 77 + kfree(path); 78 + cifs_free_open_info(&data); 79 + kfree(buf); 80 + return rc; 81 + } 82 + 83 + static int nfs_set_reparse_buf(struct reparse_posix_data *buf, 84 + mode_t mode, dev_t dev, 85 + struct kvec *iov) 86 + { 87 + u64 type; 88 + u16 len, dlen; 89 + 90 + len = sizeof(*buf); 91 + 92 + switch ((type = reparse_mode_nfs_type(mode))) { 93 + case NFS_SPECFILE_BLK: 94 + case NFS_SPECFILE_CHR: 95 + dlen = sizeof(__le64); 96 + break; 97 + case NFS_SPECFILE_FIFO: 98 + case NFS_SPECFILE_SOCK: 99 + dlen = 0; 100 + break; 101 + default: 102 + return -EOPNOTSUPP; 103 + } 104 + 105 + buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS); 106 + buf->Reserved = 0; 107 + buf->InodeType = cpu_to_le64(type); 108 + buf->ReparseDataLength = cpu_to_le16(len + dlen - 109 + sizeof(struct reparse_data_buffer)); 110 + *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | 111 + MINOR(dev)); 112 + iov->iov_base = buf; 113 + iov->iov_len = len + dlen; 114 + return 0; 115 + } 116 + 117 + int smb2_make_nfs_node(unsigned int xid, struct inode *inode, 118 + struct dentry *dentry, struct cifs_tcon *tcon, 119 + const char *full_path, umode_t mode, dev_t dev) 120 + { 121 + struct cifs_open_info_data data; 122 + struct reparse_posix_data *p; 123 + struct inode *new; 124 + struct kvec iov; 125 + __u8 buf[sizeof(*p) + sizeof(__le64)]; 126 + int rc; 127 + 128 + p = (struct reparse_posix_data *)buf; 129 + rc = nfs_set_reparse_buf(p, mode, dev, &iov); 130 + if (rc) 131 + return rc; 132 + 133 + data = (struct cifs_open_info_data) { 134 + .reparse_point = true, 135 + .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, }, 136 + }; 137 + 138 + new = smb2_get_reparse_inode(&data, inode->i_sb, xid, 139 + tcon, full_path, &iov); 140 + if (!IS_ERR(new)) 141 + d_instantiate(dentry, new); 142 + else 143 + rc = PTR_ERR(new); 144 + cifs_free_open_info(&data); 145 + return rc; 146 + } 147 + 148 + /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ 149 + static int parse_reparse_posix(struct reparse_posix_data *buf, 150 + struct cifs_sb_info *cifs_sb, 151 + struct cifs_open_info_data *data) 152 + { 153 + unsigned int len; 154 + u64 type; 155 + 156 + switch ((type = le64_to_cpu(buf->InodeType))) { 157 + case NFS_SPECFILE_LNK: 158 + len = le16_to_cpu(buf->ReparseDataLength); 159 + data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, 160 + len, true, 161 + cifs_sb->local_nls); 162 + if (!data->symlink_target) 163 + return -ENOMEM; 164 + convert_delimiter(data->symlink_target, '/'); 165 + cifs_dbg(FYI, "%s: target path: %s\n", 166 + __func__, data->symlink_target); 167 + break; 168 + case NFS_SPECFILE_CHR: 169 + case NFS_SPECFILE_BLK: 170 + case NFS_SPECFILE_FIFO: 171 + case NFS_SPECFILE_SOCK: 172 + break; 173 + default: 174 + cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", 175 + __func__, type); 176 + return -EOPNOTSUPP; 177 + } 178 + return 0; 179 + } 180 + 181 + static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, 182 + u32 plen, bool unicode, 183 + struct cifs_sb_info *cifs_sb, 184 + struct cifs_open_info_data *data) 185 + { 186 + unsigned int len; 187 + unsigned int offs; 188 + 189 + /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ 190 + 191 + offs = le16_to_cpu(sym->SubstituteNameOffset); 192 + len = le16_to_cpu(sym->SubstituteNameLength); 193 + if (offs + 20 > plen || offs + len + 20 > plen) { 194 + cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); 195 + return -EIO; 196 + } 197 + 198 + data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, 199 + len, unicode, 200 + cifs_sb->local_nls); 201 + if (!data->symlink_target) 202 + return -ENOMEM; 203 + 204 + convert_delimiter(data->symlink_target, '/'); 205 + cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); 206 + 207 + return 0; 208 + } 209 + 210 + int parse_reparse_point(struct reparse_data_buffer *buf, 211 + u32 plen, struct cifs_sb_info *cifs_sb, 212 + bool unicode, struct cifs_open_info_data *data) 213 + { 214 + data->reparse.buf = buf; 215 + 216 + /* See MS-FSCC 2.1.2 */ 217 + switch (le32_to_cpu(buf->ReparseTag)) { 218 + case IO_REPARSE_TAG_NFS: 219 + return parse_reparse_posix((struct reparse_posix_data *)buf, 220 + cifs_sb, data); 221 + case IO_REPARSE_TAG_SYMLINK: 222 + return parse_reparse_symlink( 223 + (struct reparse_symlink_data_buffer *)buf, 224 + plen, unicode, cifs_sb, data); 225 + case IO_REPARSE_TAG_LX_SYMLINK: 226 + case IO_REPARSE_TAG_AF_UNIX: 227 + case IO_REPARSE_TAG_LX_FIFO: 228 + case IO_REPARSE_TAG_LX_CHR: 229 + case IO_REPARSE_TAG_LX_BLK: 230 + return 0; 231 + default: 232 + cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", 233 + __func__, le32_to_cpu(buf->ReparseTag)); 234 + return -EOPNOTSUPP; 235 + } 236 + } 237 + 238 + int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, 239 + struct kvec *rsp_iov, 240 + struct cifs_open_info_data *data) 241 + { 242 + struct reparse_data_buffer *buf; 243 + struct smb2_ioctl_rsp *io = rsp_iov->iov_base; 244 + u32 plen = le32_to_cpu(io->OutputCount); 245 + 246 + buf = (struct reparse_data_buffer *)((u8 *)io + 247 + le32_to_cpu(io->OutputOffset)); 248 + return parse_reparse_point(buf, plen, cifs_sb, true, data); 249 + } 250 + 251 + bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, 252 + struct cifs_fattr *fattr, 253 + struct cifs_open_info_data *data) 254 + { 255 + struct reparse_posix_data *buf = data->reparse.posix; 256 + u32 tag = data->reparse.tag; 257 + 258 + if (tag == IO_REPARSE_TAG_NFS && buf) { 259 + switch (le64_to_cpu(buf->InodeType)) { 260 + case NFS_SPECFILE_CHR: 261 + fattr->cf_mode |= S_IFCHR; 262 + fattr->cf_dtype = DT_CHR; 263 + fattr->cf_rdev = reparse_nfs_mkdev(buf); 264 + break; 265 + case NFS_SPECFILE_BLK: 266 + fattr->cf_mode |= S_IFBLK; 267 + fattr->cf_dtype = DT_BLK; 268 + fattr->cf_rdev = reparse_nfs_mkdev(buf); 269 + break; 270 + case NFS_SPECFILE_FIFO: 271 + fattr->cf_mode |= S_IFIFO; 272 + fattr->cf_dtype = DT_FIFO; 273 + break; 274 + case NFS_SPECFILE_SOCK: 275 + fattr->cf_mode |= S_IFSOCK; 276 + fattr->cf_dtype = DT_SOCK; 277 + break; 278 + case NFS_SPECFILE_LNK: 279 + fattr->cf_mode |= S_IFLNK; 280 + fattr->cf_dtype = DT_LNK; 281 + break; 282 + default: 283 + WARN_ON_ONCE(1); 284 + return false; 285 + } 286 + return true; 287 + } 288 + 289 + switch (tag) { 290 + case IO_REPARSE_TAG_LX_SYMLINK: 291 + fattr->cf_mode |= S_IFLNK; 292 + fattr->cf_dtype = DT_LNK; 293 + break; 294 + case IO_REPARSE_TAG_LX_FIFO: 295 + fattr->cf_mode |= S_IFIFO; 296 + fattr->cf_dtype = DT_FIFO; 297 + break; 298 + case IO_REPARSE_TAG_AF_UNIX: 299 + fattr->cf_mode |= S_IFSOCK; 300 + fattr->cf_dtype = DT_SOCK; 301 + break; 302 + case IO_REPARSE_TAG_LX_CHR: 303 + fattr->cf_mode |= S_IFCHR; 304 + fattr->cf_dtype = DT_CHR; 305 + break; 306 + case IO_REPARSE_TAG_LX_BLK: 307 + fattr->cf_mode |= S_IFBLK; 308 + fattr->cf_dtype = DT_BLK; 309 + break; 310 + case 0: /* SMB1 symlink */ 311 + case IO_REPARSE_TAG_SYMLINK: 312 + case IO_REPARSE_TAG_NFS: 313 + fattr->cf_mode |= S_IFLNK; 314 + fattr->cf_dtype = DT_LNK; 315 + break; 316 + default: 317 + return false; 318 + } 319 + return true; 320 + }
+73
fs/smb/client/reparse.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com> 4 + */ 5 + 6 + #ifndef _CIFS_REPARSE_H 7 + #define _CIFS_REPARSE_H 8 + 9 + #include <linux/fs.h> 10 + #include <linux/stat.h> 11 + #include "cifsglob.h" 12 + 13 + static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) 14 + { 15 + u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); 16 + 17 + return MKDEV(v >> 32, v & 0xffffffff); 18 + } 19 + 20 + static inline u64 reparse_mode_nfs_type(mode_t mode) 21 + { 22 + switch (mode & S_IFMT) { 23 + case S_IFBLK: return NFS_SPECFILE_BLK; 24 + case S_IFCHR: return NFS_SPECFILE_CHR; 25 + case S_IFIFO: return NFS_SPECFILE_FIFO; 26 + case S_IFSOCK: return NFS_SPECFILE_SOCK; 27 + } 28 + return 0; 29 + } 30 + 31 + /* 32 + * Match a reparse point inode if reparse tag and ctime haven't changed. 33 + * 34 + * Windows Server updates ctime of reparse points when their data have changed. 35 + * The server doesn't allow changing reparse tags from existing reparse points, 36 + * though it's worth checking. 37 + */ 38 + static inline bool reparse_inode_match(struct inode *inode, 39 + struct cifs_fattr *fattr) 40 + { 41 + struct timespec64 ctime = inode_get_ctime(inode); 42 + 43 + return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) && 44 + CIFS_I(inode)->reparse_tag == fattr->cf_cifstag && 45 + timespec64_equal(&ctime, &fattr->cf_ctime); 46 + } 47 + 48 + static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data) 49 + { 50 + struct smb2_file_all_info *fi = &data->fi; 51 + u32 attrs = le32_to_cpu(fi->Attributes); 52 + bool ret; 53 + 54 + ret = data->reparse_point || (attrs & ATTR_REPARSE); 55 + if (ret) 56 + attrs |= ATTR_REPARSE; 57 + fi->Attributes = cpu_to_le32(attrs); 58 + return ret; 59 + } 60 + 61 + bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, 62 + struct cifs_fattr *fattr, 63 + struct cifs_open_info_data *data); 64 + int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, 65 + struct dentry *dentry, struct cifs_tcon *tcon, 66 + const char *full_path, const char *symname); 67 + int smb2_make_nfs_node(unsigned int xid, struct inode *inode, 68 + struct dentry *dentry, struct cifs_tcon *tcon, 69 + const char *full_path, umode_t mode, dev_t dev); 70 + int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov, 71 + struct cifs_open_info_data *data); 72 + 73 + #endif /* _CIFS_REPARSE_H */
+3 -251
fs/smb/client/smb2ops.c
··· 28 28 #include "fscache.h" 29 29 #include "fs_context.h" 30 30 #include "cached_dir.h" 31 + #include "reparse.h" 31 32 32 33 /* Change credits for different ops and return the total number of credits */ 33 34 static int ··· 2987 2986 return rc; 2988 2987 } 2989 2988 2990 - /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ 2991 - static int parse_reparse_posix(struct reparse_posix_data *buf, 2992 - struct cifs_sb_info *cifs_sb, 2993 - struct cifs_open_info_data *data) 2994 - { 2995 - unsigned int len; 2996 - u64 type; 2997 - 2998 - switch ((type = le64_to_cpu(buf->InodeType))) { 2999 - case NFS_SPECFILE_LNK: 3000 - len = le16_to_cpu(buf->ReparseDataLength); 3001 - data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, 3002 - len, true, 3003 - cifs_sb->local_nls); 3004 - if (!data->symlink_target) 3005 - return -ENOMEM; 3006 - convert_delimiter(data->symlink_target, '/'); 3007 - cifs_dbg(FYI, "%s: target path: %s\n", 3008 - __func__, data->symlink_target); 3009 - break; 3010 - case NFS_SPECFILE_CHR: 3011 - case NFS_SPECFILE_BLK: 3012 - case NFS_SPECFILE_FIFO: 3013 - case NFS_SPECFILE_SOCK: 3014 - break; 3015 - default: 3016 - cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", 3017 - __func__, type); 3018 - return -EOPNOTSUPP; 3019 - } 3020 - return 0; 3021 - } 3022 - 3023 - static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, 3024 - u32 plen, bool unicode, 3025 - struct cifs_sb_info *cifs_sb, 3026 - struct cifs_open_info_data *data) 3027 - { 3028 - unsigned int len; 3029 - unsigned int offs; 3030 - 3031 - /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ 3032 - 3033 - offs = le16_to_cpu(sym->SubstituteNameOffset); 3034 - len = le16_to_cpu(sym->SubstituteNameLength); 3035 - if (offs + 20 > plen || offs + len + 20 > plen) { 3036 - cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); 3037 - return -EIO; 3038 - } 3039 - 3040 - data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, 3041 - len, unicode, 3042 - cifs_sb->local_nls); 3043 - if (!data->symlink_target) 3044 - return -ENOMEM; 3045 - 3046 - convert_delimiter(data->symlink_target, '/'); 3047 - cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); 3048 - 3049 - return 0; 3050 - } 3051 - 3052 - int parse_reparse_point(struct reparse_data_buffer *buf, 3053 - u32 plen, struct cifs_sb_info *cifs_sb, 3054 - bool unicode, struct cifs_open_info_data *data) 3055 - { 3056 - data->reparse.buf = buf; 3057 - 3058 - /* See MS-FSCC 2.1.2 */ 3059 - switch (le32_to_cpu(buf->ReparseTag)) { 3060 - case IO_REPARSE_TAG_NFS: 3061 - return parse_reparse_posix((struct reparse_posix_data *)buf, 3062 - cifs_sb, data); 3063 - case IO_REPARSE_TAG_SYMLINK: 3064 - return parse_reparse_symlink( 3065 - (struct reparse_symlink_data_buffer *)buf, 3066 - plen, unicode, cifs_sb, data); 3067 - case IO_REPARSE_TAG_LX_SYMLINK: 3068 - case IO_REPARSE_TAG_AF_UNIX: 3069 - case IO_REPARSE_TAG_LX_FIFO: 3070 - case IO_REPARSE_TAG_LX_CHR: 3071 - case IO_REPARSE_TAG_LX_BLK: 3072 - return 0; 3073 - default: 3074 - cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", 3075 - __func__, le32_to_cpu(buf->ReparseTag)); 3076 - return -EOPNOTSUPP; 3077 - } 3078 - } 3079 - 3080 - static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, 3081 - struct kvec *rsp_iov, 3082 - struct cifs_open_info_data *data) 3083 - { 3084 - struct reparse_data_buffer *buf; 3085 - struct smb2_ioctl_rsp *io = rsp_iov->iov_base; 3086 - u32 plen = le32_to_cpu(io->OutputCount); 3087 - 3088 - buf = (struct reparse_data_buffer *)((u8 *)io + 3089 - le32_to_cpu(io->OutputOffset)); 3090 - return parse_reparse_point(buf, plen, cifs_sb, true, data); 3091 - } 3092 - 3093 2989 static struct cifs_ntsd * 3094 2990 get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, 3095 2991 const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) ··· 5026 5128 return rc; 5027 5129 } 5028 5130 5029 - static inline u64 mode_nfs_type(mode_t mode) 5030 - { 5031 - switch (mode & S_IFMT) { 5032 - case S_IFBLK: return NFS_SPECFILE_BLK; 5033 - case S_IFCHR: return NFS_SPECFILE_CHR; 5034 - case S_IFIFO: return NFS_SPECFILE_FIFO; 5035 - case S_IFSOCK: return NFS_SPECFILE_SOCK; 5036 - } 5037 - return 0; 5038 - } 5039 - 5040 - static int nfs_set_reparse_buf(struct reparse_posix_data *buf, 5041 - mode_t mode, dev_t dev, 5042 - struct kvec *iov) 5043 - { 5044 - u64 type; 5045 - u16 len, dlen; 5046 - 5047 - len = sizeof(*buf); 5048 - 5049 - switch ((type = mode_nfs_type(mode))) { 5050 - case NFS_SPECFILE_BLK: 5051 - case NFS_SPECFILE_CHR: 5052 - dlen = sizeof(__le64); 5053 - break; 5054 - case NFS_SPECFILE_FIFO: 5055 - case NFS_SPECFILE_SOCK: 5056 - dlen = 0; 5057 - break; 5058 - default: 5059 - return -EOPNOTSUPP; 5060 - } 5061 - 5062 - buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS); 5063 - buf->Reserved = 0; 5064 - buf->InodeType = cpu_to_le64(type); 5065 - buf->ReparseDataLength = cpu_to_le16(len + dlen - 5066 - sizeof(struct reparse_data_buffer)); 5067 - *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | 5068 - MINOR(dev)); 5069 - iov->iov_base = buf; 5070 - iov->iov_len = len + dlen; 5071 - return 0; 5072 - } 5073 - 5074 - static int nfs_make_node(unsigned int xid, struct inode *inode, 5075 - struct dentry *dentry, struct cifs_tcon *tcon, 5076 - const char *full_path, umode_t mode, dev_t dev) 5077 - { 5078 - struct cifs_open_info_data data; 5079 - struct reparse_posix_data *p; 5080 - struct inode *new; 5081 - struct kvec iov; 5082 - __u8 buf[sizeof(*p) + sizeof(__le64)]; 5083 - int rc; 5084 - 5085 - p = (struct reparse_posix_data *)buf; 5086 - rc = nfs_set_reparse_buf(p, mode, dev, &iov); 5087 - if (rc) 5088 - return rc; 5089 - 5090 - data = (struct cifs_open_info_data) { 5091 - .reparse_point = true, 5092 - .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, }, 5093 - }; 5094 - 5095 - new = smb2_get_reparse_inode(&data, inode->i_sb, xid, 5096 - tcon, full_path, &iov); 5097 - if (!IS_ERR(new)) 5098 - d_instantiate(dentry, new); 5099 - else 5100 - rc = PTR_ERR(new); 5101 - cifs_free_open_info(&data); 5102 - return rc; 5103 - } 5104 - 5105 - static int smb2_create_reparse_symlink(const unsigned int xid, 5106 - struct inode *inode, 5107 - struct dentry *dentry, 5108 - struct cifs_tcon *tcon, 5109 - const char *full_path, 5110 - const char *symname) 5111 - { 5112 - struct reparse_symlink_data_buffer *buf = NULL; 5113 - struct cifs_open_info_data data; 5114 - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 5115 - struct inode *new; 5116 - struct kvec iov; 5117 - __le16 *path; 5118 - char *sym, sep = CIFS_DIR_SEP(cifs_sb); 5119 - u16 len, plen; 5120 - int rc = 0; 5121 - 5122 - sym = kstrdup(symname, GFP_KERNEL); 5123 - if (!sym) 5124 - return -ENOMEM; 5125 - 5126 - data = (struct cifs_open_info_data) { 5127 - .reparse_point = true, 5128 - .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, }, 5129 - .symlink_target = sym, 5130 - }; 5131 - 5132 - convert_delimiter(sym, sep); 5133 - path = cifs_convert_path_to_utf16(sym, cifs_sb); 5134 - if (!path) { 5135 - rc = -ENOMEM; 5136 - goto out; 5137 - } 5138 - 5139 - plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); 5140 - len = sizeof(*buf) + plen * 2; 5141 - buf = kzalloc(len, GFP_KERNEL); 5142 - if (!buf) { 5143 - rc = -ENOMEM; 5144 - goto out; 5145 - } 5146 - 5147 - buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK); 5148 - buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer)); 5149 - buf->SubstituteNameOffset = cpu_to_le16(plen); 5150 - buf->SubstituteNameLength = cpu_to_le16(plen); 5151 - memcpy(&buf->PathBuffer[plen], path, plen); 5152 - buf->PrintNameOffset = 0; 5153 - buf->PrintNameLength = cpu_to_le16(plen); 5154 - memcpy(buf->PathBuffer, path, plen); 5155 - buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); 5156 - if (*sym != sep) 5157 - buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE); 5158 - 5159 - convert_delimiter(sym, '/'); 5160 - iov.iov_base = buf; 5161 - iov.iov_len = len; 5162 - new = smb2_get_reparse_inode(&data, inode->i_sb, xid, 5163 - tcon, full_path, &iov); 5164 - if (!IS_ERR(new)) 5165 - d_instantiate(dentry, new); 5166 - else 5167 - rc = PTR_ERR(new); 5168 - out: 5169 - kfree(path); 5170 - cifs_free_open_info(&data); 5171 - kfree(buf); 5172 - return rc; 5173 - } 5174 - 5175 5131 static int smb2_make_node(unsigned int xid, struct inode *inode, 5176 5132 struct dentry *dentry, struct cifs_tcon *tcon, 5177 5133 const char *full_path, umode_t mode, dev_t dev) ··· 5043 5291 rc = cifs_sfu_make_node(xid, inode, dentry, tcon, 5044 5292 full_path, mode, dev); 5045 5293 } else { 5046 - rc = nfs_make_node(xid, inode, dentry, tcon, 5047 - full_path, mode, dev); 5294 + rc = smb2_make_nfs_node(xid, inode, dentry, tcon, 5295 + full_path, mode, dev); 5048 5296 } 5049 5297 return rc; 5050 5298 }
+6
fs/smb/client/smb2proto.h
··· 310 310 int posix_info_parse(const void *beg, const void *end, 311 311 struct smb2_posix_info_parsed *out); 312 312 int posix_info_sid_size(const void *beg, const void *end); 313 + int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, 314 + struct dentry *dentry, struct cifs_tcon *tcon, 315 + const char *full_path, const char *symname); 316 + int smb2_make_nfs_node(unsigned int xid, struct inode *inode, 317 + struct dentry *dentry, struct cifs_tcon *tcon, 318 + const char *full_path, umode_t mode, dev_t dev); 313 319 314 320 #endif /* _SMB2PROTO_H */