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 <linux/uidgid.h> 12 #include "fs_context.h" 13 #include "cifsglob.h" 14 15 /* 16 * Used only by cifs.ko to ignore reparse points from files when client or 17 * server doesn't support FSCTL_GET_REPARSE_POINT. 18 */ 19 #define IO_REPARSE_TAG_INTERNAL ((__u32)~0U) 20 21 static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) 22 { 23 u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); 24 25 return MKDEV(v >> 32, v & 0xffffffff); 26 } 27 28 static inline dev_t wsl_mkdev(void *ptr) 29 { 30 u64 v = le64_to_cpu(*(__le64 *)ptr); 31 32 return MKDEV(v & 0xffffffff, v >> 32); 33 } 34 35 static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb, 36 void *ptr) 37 { 38 u32 uid = le32_to_cpu(*(__le32 *)ptr); 39 40 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) 41 return cifs_sb->ctx->linux_uid; 42 return make_kuid(current_user_ns(), uid); 43 } 44 45 static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb, 46 void *ptr) 47 { 48 u32 gid = le32_to_cpu(*(__le32 *)ptr); 49 50 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) 51 return cifs_sb->ctx->linux_gid; 52 return make_kgid(current_user_ns(), gid); 53 } 54 55 static inline u64 reparse_mode_nfs_type(mode_t mode) 56 { 57 switch (mode & S_IFMT) { 58 case S_IFBLK: return NFS_SPECFILE_BLK; 59 case S_IFCHR: return NFS_SPECFILE_CHR; 60 case S_IFIFO: return NFS_SPECFILE_FIFO; 61 case S_IFSOCK: return NFS_SPECFILE_SOCK; 62 } 63 return 0; 64 } 65 66 static inline u32 reparse_mode_wsl_tag(mode_t mode) 67 { 68 switch (mode & S_IFMT) { 69 case S_IFBLK: return IO_REPARSE_TAG_LX_BLK; 70 case S_IFCHR: return IO_REPARSE_TAG_LX_CHR; 71 case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO; 72 case S_IFSOCK: return IO_REPARSE_TAG_AF_UNIX; 73 } 74 return 0; 75 } 76 77 /* 78 * Match a reparse point inode if reparse tag and ctime haven't changed. 79 * 80 * Windows Server updates ctime of reparse points when their data have changed. 81 * The server doesn't allow changing reparse tags from existing reparse points, 82 * though it's worth checking. 83 */ 84 static inline bool reparse_inode_match(struct inode *inode, 85 struct cifs_fattr *fattr) 86 { 87 struct cifsInodeInfo *cinode = CIFS_I(inode); 88 struct timespec64 ctime = inode_get_ctime(inode); 89 90 /* 91 * Do not match reparse tags when client or server doesn't support 92 * FSCTL_GET_REPARSE_POINT. @fattr->cf_cifstag should contain correct 93 * reparse tag from query dir response but the client won't be able to 94 * read the reparse point data anyway. This spares us a revalidation. 95 */ 96 if (cinode->reparse_tag != IO_REPARSE_TAG_INTERNAL && 97 cinode->reparse_tag != fattr->cf_cifstag) 98 return false; 99 return (cinode->cifsAttrs & ATTR_REPARSE) && 100 timespec64_equal(&ctime, &fattr->cf_ctime); 101 } 102 103 static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data) 104 { 105 struct smb2_file_all_info *fi = &data->fi; 106 u32 attrs = le32_to_cpu(fi->Attributes); 107 bool ret; 108 109 ret = data->reparse_point || (attrs & ATTR_REPARSE); 110 if (ret) 111 attrs |= ATTR_REPARSE; 112 fi->Attributes = cpu_to_le32(attrs); 113 return ret; 114 } 115 116 bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, 117 struct cifs_fattr *fattr, 118 struct cifs_open_info_data *data); 119 int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, 120 struct dentry *dentry, struct cifs_tcon *tcon, 121 const char *full_path, const char *symname); 122 int smb2_mknod_reparse(unsigned int xid, struct inode *inode, 123 struct dentry *dentry, struct cifs_tcon *tcon, 124 const char *full_path, umode_t mode, dev_t dev); 125 int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov, 126 struct cifs_open_info_data *data); 127 128 #endif /* _CIFS_REPARSE_H */ 129