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