1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de> 4 */ 5 6 #ifndef _CIFS_DFS_H 7 #define _CIFS_DFS_H 8 9 #include "cifsglob.h" 10 #include "cifsproto.h" 11 #include "fs_context.h" 12 #include "dfs_cache.h" 13 #include "cifs_unicode.h" 14 #include <linux/namei.h> 15 #include <linux/errno.h> 16 17 #define DFS_INTERLINK(v) \ 18 (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) 19 20 struct dfs_ref { 21 char *path; 22 char *full_path; 23 struct cifs_ses *ses; 24 struct dfs_cache_tgt_list tl; 25 struct dfs_cache_tgt_iterator *tit; 26 }; 27 28 struct dfs_ref_walk { 29 struct cifs_mount_ctx *mnt_ctx; 30 struct dfs_ref *ref; 31 struct dfs_ref refs[MAX_NESTED_LINKS]; 32 }; 33 34 #define ref_walk_start(w) ((w)->refs) 35 #define ref_walk_end(w) (&(w)->refs[ARRAY_SIZE((w)->refs) - 1]) 36 #define ref_walk_cur(w) ((w)->ref) 37 #define ref_walk_descend(w) (--ref_walk_cur(w) >= ref_walk_start(w)) 38 39 #define ref_walk_tit(w) (ref_walk_cur(w)->tit) 40 #define ref_walk_path(w) (ref_walk_cur(w)->path) 41 #define ref_walk_fpath(w) (ref_walk_cur(w)->full_path) 42 #define ref_walk_tl(w) (&ref_walk_cur(w)->tl) 43 #define ref_walk_ses(w) (ref_walk_cur(w)->ses) 44 45 static inline struct dfs_ref_walk *ref_walk_alloc(void) 46 { 47 struct dfs_ref_walk *rw; 48 49 rw = kmalloc(sizeof(*rw), GFP_KERNEL); 50 if (!rw) 51 return ERR_PTR(-ENOMEM); 52 return rw; 53 } 54 55 static inline void ref_walk_init(struct dfs_ref_walk *rw, 56 struct cifs_mount_ctx *mnt_ctx) 57 { 58 memset(rw, 0, sizeof(*rw)); 59 rw->mnt_ctx = mnt_ctx; 60 ref_walk_cur(rw) = ref_walk_start(rw); 61 } 62 63 static inline void __ref_walk_free(struct dfs_ref *ref) 64 { 65 kfree(ref->path); 66 kfree(ref->full_path); 67 dfs_cache_free_tgts(&ref->tl); 68 if (ref->ses) 69 cifs_put_smb_ses(ref->ses); 70 memset(ref, 0, sizeof(*ref)); 71 } 72 73 static inline void ref_walk_free(struct dfs_ref_walk *rw) 74 { 75 struct dfs_ref *ref; 76 77 if (!rw) 78 return; 79 80 for (ref = ref_walk_start(rw); ref <= ref_walk_end(rw); ref++) 81 __ref_walk_free(ref); 82 kfree(rw); 83 } 84 85 static inline int ref_walk_advance(struct dfs_ref_walk *rw) 86 { 87 struct dfs_ref *ref = ref_walk_cur(rw) + 1; 88 89 if (ref > ref_walk_end(rw)) 90 return -ELOOP; 91 __ref_walk_free(ref); 92 ref_walk_cur(rw) = ref; 93 return 0; 94 } 95 96 static inline struct dfs_cache_tgt_iterator * 97 ref_walk_next_tgt(struct dfs_ref_walk *rw) 98 { 99 struct dfs_ref *ref = ref_walk_cur(rw); 100 struct dfs_cache_tgt_iterator *tit; 101 102 if (IS_ERR(ref->tit)) 103 return NULL; 104 105 if (!ref->tit) 106 tit = dfs_cache_get_tgt_iterator(&ref->tl); 107 else 108 tit = dfs_cache_get_next_tgt(&ref->tl, ref->tit); 109 110 if (!tit) { 111 ref->tit = ERR_PTR(-ENOENT); 112 return NULL; 113 } 114 ref->tit = tit; 115 return ref->tit; 116 } 117 118 static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw, 119 struct dfs_info3_param *tgt) 120 { 121 zfree_dfs_info_param(tgt); 122 return dfs_cache_get_tgt_referral(ref_walk_path(rw) + 1, 123 ref_walk_tit(rw), tgt); 124 } 125 126 static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw) 127 { 128 dfs_cache_noreq_update_tgthint(ref_walk_path(rw) + 1, 129 ref_walk_tit(rw)); 130 } 131 132 static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw, 133 struct cifs_tcon *tcon) 134 { 135 struct dfs_ref *ref = ref_walk_start(rw); 136 137 for (; ref <= ref_walk_cur(rw); ref++) { 138 if (WARN_ON_ONCE(!ref->ses)) 139 continue; 140 list_add(&ref->ses->dlist, &tcon->dfs_ses_list); 141 ref->ses = NULL; 142 } 143 } 144 145 static inline void ref_walk_mark_end(struct dfs_ref_walk *rw) 146 { 147 struct dfs_ref *ref = ref_walk_cur(rw) - 1; 148 149 WARN_ON_ONCE(ref < ref_walk_start(rw)); 150 dfs_cache_noreq_update_tgthint(ref->path + 1, ref->tit); 151 ref->tit = ERR_PTR(-ENOENT); /* end marker */ 152 } 153 154 int dfs_parse_target_referral(const char *full_path, 155 const struct dfs_info3_param *ref, 156 struct smb3_fs_context *ctx); 157 int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx); 158 159 static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path) 160 { 161 return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb)); 162 } 163 164 static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, 165 const char *path, 166 struct dfs_cache_tgt_list *tl) 167 { 168 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 169 struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 170 struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses; 171 172 return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls, 173 cifs_remap(cifs_sb), path, NULL, tl); 174 } 175 176 /* 177 * cifs_get_smb_ses() already guarantees an active reference of 178 * @ses->dfs_root_ses when a new session is created, so we need to put extra 179 * references of all DFS root sessions that were used across the mount process 180 * in dfs_mount_share(). 181 */ 182 static inline void dfs_put_root_smb_sessions(struct list_head *head) 183 { 184 struct cifs_ses *ses, *n; 185 186 list_for_each_entry_safe(ses, n, head, dlist) { 187 list_del_init(&ses->dlist); 188 cifs_put_smb_ses(ses); 189 } 190 } 191 192 static inline const char *dfs_ses_refpath(struct cifs_ses *ses) 193 { 194 const char *path = ses->server->leaf_fullpath; 195 196 return path ? path + 1 : ERR_PTR(-ENOENT); 197 } 198 199 #endif /* _CIFS_DFS_H */ 200