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, const struct dfs_info3_param *ref, 155 struct smb3_fs_context *ctx); 156 int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx); 157 158 static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path) 159 { 160 return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb)); 161 } 162 163 static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, 164 const char *path, 165 struct dfs_cache_tgt_list *tl) 166 { 167 struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; 168 struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; 169 struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses; 170 171 return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls, 172 cifs_remap(cifs_sb), path, NULL, tl); 173 } 174 175 /* 176 * cifs_get_smb_ses() already guarantees an active reference of 177 * @ses->dfs_root_ses when a new session is created, so we need to put extra 178 * references of all DFS root sessions that were used across the mount process 179 * in dfs_mount_share(). 180 */ 181 static inline void dfs_put_root_smb_sessions(struct list_head *head) 182 { 183 struct cifs_ses *ses, *n; 184 185 list_for_each_entry_safe(ses, n, head, dlist) { 186 list_del_init(&ses->dlist); 187 cifs_put_smb_ses(ses); 188 } 189 } 190 191 static inline const char *dfs_ses_refpath(struct cifs_ses *ses) 192 { 193 const char *path = ses->server->leaf_fullpath; 194 195 return path ? path + 1 : ERR_PTR(-ENOENT); 196 } 197 198 #endif /* _CIFS_DFS_H */ 199