1db0f1c07SPaulo Alcantara /* SPDX-License-Identifier: GPL-2.0-only */
2db0f1c07SPaulo Alcantara /*
3db0f1c07SPaulo Alcantara * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
4db0f1c07SPaulo Alcantara */
5db0f1c07SPaulo Alcantara
6db0f1c07SPaulo Alcantara #ifndef _CIFS_REPARSE_H
7db0f1c07SPaulo Alcantara #define _CIFS_REPARSE_H
8db0f1c07SPaulo Alcantara
9db0f1c07SPaulo Alcantara #include <linux/fs.h>
10db0f1c07SPaulo Alcantara #include <linux/stat.h>
1197db4160SPaulo Alcantara #include <linux/uidgid.h>
1297db4160SPaulo Alcantara #include "fs_context.h"
13db0f1c07SPaulo Alcantara #include "cifsglob.h"
14db0f1c07SPaulo Alcantara
15921d9090SPaulo Alcantara #define REPARSE_SYM_PATH_MAX 4060
16921d9090SPaulo Alcantara
170397a48cSPaulo Alcantara /*
180397a48cSPaulo Alcantara * Used only by cifs.ko to ignore reparse points from files when client or
190397a48cSPaulo Alcantara * server doesn't support FSCTL_GET_REPARSE_POINT.
200397a48cSPaulo Alcantara */
210397a48cSPaulo Alcantara #define IO_REPARSE_TAG_INTERNAL ((__u32)~0U)
220397a48cSPaulo Alcantara
reparse_mkdev(void * ptr)23bbc258dcSPaulo Alcantara static inline dev_t reparse_mkdev(void *ptr)
2497db4160SPaulo Alcantara {
2597db4160SPaulo Alcantara u64 v = le64_to_cpu(*(__le64 *)ptr);
2697db4160SPaulo Alcantara
2797db4160SPaulo Alcantara return MKDEV(v & 0xffffffff, v >> 32);
2897db4160SPaulo Alcantara }
2997db4160SPaulo Alcantara
wsl_make_kuid(struct cifs_sb_info * cifs_sb,void * ptr)3097db4160SPaulo Alcantara static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb,
3197db4160SPaulo Alcantara void *ptr)
3297db4160SPaulo Alcantara {
3397db4160SPaulo Alcantara u32 uid = le32_to_cpu(*(__le32 *)ptr);
3497db4160SPaulo Alcantara
3597db4160SPaulo Alcantara if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
3697db4160SPaulo Alcantara return cifs_sb->ctx->linux_uid;
3797db4160SPaulo Alcantara return make_kuid(current_user_ns(), uid);
3897db4160SPaulo Alcantara }
3997db4160SPaulo Alcantara
wsl_make_kgid(struct cifs_sb_info * cifs_sb,void * ptr)4097db4160SPaulo Alcantara static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb,
4197db4160SPaulo Alcantara void *ptr)
4297db4160SPaulo Alcantara {
4397db4160SPaulo Alcantara u32 gid = le32_to_cpu(*(__le32 *)ptr);
4497db4160SPaulo Alcantara
4597db4160SPaulo Alcantara if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
4697db4160SPaulo Alcantara return cifs_sb->ctx->linux_gid;
4797db4160SPaulo Alcantara return make_kgid(current_user_ns(), gid);
4897db4160SPaulo Alcantara }
4997db4160SPaulo Alcantara
reparse_mode_nfs_type(mode_t mode)50db0f1c07SPaulo Alcantara static inline u64 reparse_mode_nfs_type(mode_t mode)
51db0f1c07SPaulo Alcantara {
52db0f1c07SPaulo Alcantara switch (mode & S_IFMT) {
53db0f1c07SPaulo Alcantara case S_IFBLK: return NFS_SPECFILE_BLK;
54db0f1c07SPaulo Alcantara case S_IFCHR: return NFS_SPECFILE_CHR;
55db0f1c07SPaulo Alcantara case S_IFIFO: return NFS_SPECFILE_FIFO;
56db0f1c07SPaulo Alcantara case S_IFSOCK: return NFS_SPECFILE_SOCK;
57db0f1c07SPaulo Alcantara }
58db0f1c07SPaulo Alcantara return 0;
59db0f1c07SPaulo Alcantara }
60db0f1c07SPaulo Alcantara
reparse_mode_wsl_tag(mode_t mode)61df0a8a19SPaulo Alcantara static inline u32 reparse_mode_wsl_tag(mode_t mode)
62df0a8a19SPaulo Alcantara {
63df0a8a19SPaulo Alcantara switch (mode & S_IFMT) {
64df0a8a19SPaulo Alcantara case S_IFBLK: return IO_REPARSE_TAG_LX_BLK;
65df0a8a19SPaulo Alcantara case S_IFCHR: return IO_REPARSE_TAG_LX_CHR;
66df0a8a19SPaulo Alcantara case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO;
67df0a8a19SPaulo Alcantara case S_IFSOCK: return IO_REPARSE_TAG_AF_UNIX;
68df0a8a19SPaulo Alcantara }
69df0a8a19SPaulo Alcantara return 0;
70df0a8a19SPaulo Alcantara }
71df0a8a19SPaulo Alcantara
72db0f1c07SPaulo Alcantara /*
73db0f1c07SPaulo Alcantara * Match a reparse point inode if reparse tag and ctime haven't changed.
74db0f1c07SPaulo Alcantara *
75db0f1c07SPaulo Alcantara * Windows Server updates ctime of reparse points when their data have changed.
76db0f1c07SPaulo Alcantara * The server doesn't allow changing reparse tags from existing reparse points,
77db0f1c07SPaulo Alcantara * though it's worth checking.
78db0f1c07SPaulo Alcantara */
reparse_inode_match(struct inode * inode,struct cifs_fattr * fattr)79db0f1c07SPaulo Alcantara static inline bool reparse_inode_match(struct inode *inode,
80db0f1c07SPaulo Alcantara struct cifs_fattr *fattr)
81db0f1c07SPaulo Alcantara {
820397a48cSPaulo Alcantara struct cifsInodeInfo *cinode = CIFS_I(inode);
83db0f1c07SPaulo Alcantara struct timespec64 ctime = inode_get_ctime(inode);
84db0f1c07SPaulo Alcantara
850397a48cSPaulo Alcantara /*
860397a48cSPaulo Alcantara * Do not match reparse tags when client or server doesn't support
870397a48cSPaulo Alcantara * FSCTL_GET_REPARSE_POINT. @fattr->cf_cifstag should contain correct
880397a48cSPaulo Alcantara * reparse tag from query dir response but the client won't be able to
890397a48cSPaulo Alcantara * read the reparse point data anyway. This spares us a revalidation.
900397a48cSPaulo Alcantara */
910397a48cSPaulo Alcantara if (cinode->reparse_tag != IO_REPARSE_TAG_INTERNAL &&
920397a48cSPaulo Alcantara cinode->reparse_tag != fattr->cf_cifstag)
930397a48cSPaulo Alcantara return false;
940397a48cSPaulo Alcantara return (cinode->cifsAttrs & ATTR_REPARSE) &&
95db0f1c07SPaulo Alcantara timespec64_equal(&ctime, &fattr->cf_ctime);
96db0f1c07SPaulo Alcantara }
97db0f1c07SPaulo Alcantara
cifs_open_data_reparse(struct cifs_open_info_data * data)98db0f1c07SPaulo Alcantara static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
99db0f1c07SPaulo Alcantara {
100db0f1c07SPaulo Alcantara struct smb2_file_all_info *fi = &data->fi;
101db0f1c07SPaulo Alcantara u32 attrs = le32_to_cpu(fi->Attributes);
102db0f1c07SPaulo Alcantara bool ret;
103db0f1c07SPaulo Alcantara
104db0f1c07SPaulo Alcantara ret = data->reparse_point || (attrs & ATTR_REPARSE);
105db0f1c07SPaulo Alcantara if (ret)
106db0f1c07SPaulo Alcantara attrs |= ATTR_REPARSE;
107db0f1c07SPaulo Alcantara fi->Attributes = cpu_to_le32(attrs);
108db0f1c07SPaulo Alcantara return ret;
109db0f1c07SPaulo Alcantara }
110db0f1c07SPaulo Alcantara
111db0f1c07SPaulo Alcantara bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
112db0f1c07SPaulo Alcantara struct cifs_fattr *fattr,
113db0f1c07SPaulo Alcantara struct cifs_open_info_data *data);
114db0f1c07SPaulo Alcantara int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
115db0f1c07SPaulo Alcantara struct dentry *dentry, struct cifs_tcon *tcon,
116db0f1c07SPaulo Alcantara const char *full_path, const char *symname);
117df0a8a19SPaulo Alcantara int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
118db0f1c07SPaulo Alcantara struct dentry *dentry, struct cifs_tcon *tcon,
119db0f1c07SPaulo Alcantara const char *full_path, umode_t mode, dev_t dev);
120*430afd3eSPali Rohár int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
121*430afd3eSPali Rohár const char *full_path,
122*430afd3eSPali Rohár struct kvec *rsp_iov,
123db0f1c07SPaulo Alcantara struct cifs_open_info_data *data);
124db0f1c07SPaulo Alcantara
125db0f1c07SPaulo Alcantara #endif /* _CIFS_REPARSE_H */
126