]>
Commit | Line | Data |
---|---|---|
de8ee011 A |
1 | // |
2 | // lf_hfs_lookup.c | |
3 | // livefiles_hfs | |
4 | // | |
5 | // Created by Yakov Ben Zaken on 25/03/2018. | |
6 | // | |
7 | ||
8 | #include "lf_hfs.h" | |
9 | #include "lf_hfs_lookup.h" | |
10 | #include "lf_hfs_cnode.h" | |
11 | #include "lf_hfs_vfsutils.h" | |
12 | #include "lf_hfs_link.h" | |
13 | ||
14 | static int | |
15 | hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int *cnode_locked) | |
16 | { | |
17 | struct cnode *dcp; /* cnode for directory being searched */ | |
18 | struct vnode *tvp; /* target vnode */ | |
19 | struct hfsmount *hfsmp; | |
20 | int flags; | |
21 | int nameiop; | |
22 | int retval = 0; | |
23 | struct cat_desc desc; | |
24 | struct cat_desc cndesc; | |
25 | struct cat_attr attr; | |
26 | struct cat_fork fork; | |
27 | int lockflags; | |
28 | int newvnode_flags = 0; | |
29 | ||
30 | retry: | |
31 | newvnode_flags = 0; | |
32 | dcp = NULL; | |
33 | hfsmp = VTOHFS(dvp); | |
34 | *vpp = NULL; | |
35 | *cnode_locked = 0; | |
36 | tvp = NULL; | |
37 | nameiop = cnp->cn_nameiop; | |
38 | flags = cnp->cn_flags; | |
39 | bzero(&desc, sizeof(desc)); | |
40 | ||
41 | if (hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) { | |
42 | retval = ENOENT; /* The parent no longer exists ? */ | |
43 | goto exit; | |
44 | } | |
45 | dcp = VTOC(dvp); | |
46 | ||
47 | /* | |
48 | * Need to understand if we need this check.. as we took exclusive lock.. | |
49 | */ | |
50 | if (dcp->c_flag & C_DIR_MODIFICATION){ | |
51 | hfs_unlock(dcp); | |
52 | usleep( 1000 ); | |
53 | goto retry; | |
54 | } | |
55 | ||
56 | /* | |
57 | * We shouldn't need to go to the catalog if there are no children. | |
58 | * However, in the face of a minor disk corruption where the valence of | |
59 | * the directory is off, we could infinite loop here if we return ENOENT | |
60 | * even though there are actually items in the directory. (create will | |
61 | * see the ENOENT, try to create something, which will return with | |
62 | * EEXIST over and over again). As a result, always check the catalog. | |
63 | */ | |
64 | ||
65 | bzero(&cndesc, sizeof(cndesc)); | |
66 | cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; | |
67 | cndesc.cd_namelen = cnp->cn_namelen; | |
68 | cndesc.cd_parentcnid = dcp->c_fileid; | |
69 | cndesc.cd_hint = dcp->c_childhint; | |
70 | ||
71 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
72 | retval = cat_lookup(hfsmp, &cndesc, 0, &desc, &attr, &fork, NULL); | |
73 | hfs_systemfile_unlock(hfsmp, lockflags); | |
74 | ||
75 | if (retval == 0) { | |
76 | dcp->c_childhint = desc.cd_hint; | |
77 | /* | |
78 | * Note: We must drop the parent lock here before calling | |
79 | * hfs_getnewvnode (which takes the child lock). | |
80 | */ | |
81 | hfs_unlock(dcp); | |
82 | dcp = NULL; | |
83 | ||
84 | /* Verify that the item just looked up isn't one of the hidden directories. */ | |
85 | if (desc.cd_cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid || | |
86 | desc.cd_cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { | |
87 | retval = ENOENT; | |
88 | goto exit; | |
89 | } | |
90 | goto found; | |
91 | } | |
92 | ||
93 | if (retval == HFS_ERESERVEDNAME) { | |
94 | /* | |
95 | * We found the name in the catalog, but it is unavailable | |
96 | * to us. The exact error to return to our caller depends | |
97 | * on the operation, and whether we've already reached the | |
98 | * last path component. In all cases, avoid a negative | |
99 | * cache entry, since someone else may be able to access | |
100 | * the name if their lookup is configured differently. | |
101 | */ | |
102 | ||
103 | cnp->cn_flags &= ~MAKEENTRY; | |
104 | ||
105 | if (((flags & ISLASTCN) == 0) || ((nameiop == LOOKUP) || (nameiop == DELETE))) { | |
106 | /* A reserved name for a pure lookup is the same as the path not being present */ | |
107 | retval = ENOENT; | |
108 | } else { | |
109 | /* A reserved name with intent to create must be rejected as impossible */ | |
110 | retval = EEXIST; | |
111 | } | |
112 | } | |
113 | if (retval != ENOENT) | |
114 | goto exit; | |
115 | /* | |
116 | * This is a non-existing entry | |
117 | * | |
118 | * If creating, and at end of pathname and current | |
119 | * directory has not been removed, then can consider | |
120 | * allowing file to be created. | |
121 | */ | |
122 | if ((nameiop == CREATE || nameiop == RENAME) && | |
123 | (flags & ISLASTCN) && | |
124 | !(ISSET(dcp->c_flag, C_DELETED | C_NOEXISTS))) { | |
125 | retval = EJUSTRETURN; | |
126 | goto exit; | |
127 | } | |
128 | ||
129 | goto exit; | |
130 | ||
131 | found: | |
132 | if (flags & ISLASTCN) { | |
133 | switch(nameiop) { | |
134 | case DELETE: | |
135 | cnp->cn_flags &= ~MAKEENTRY; | |
136 | break; | |
137 | ||
138 | case RENAME: | |
139 | cnp->cn_flags &= ~MAKEENTRY; | |
140 | break; | |
141 | default: | |
142 | break; | |
143 | } | |
144 | } | |
145 | ||
146 | int type = (attr.ca_mode & S_IFMT); | |
147 | ||
148 | if (!(flags & ISLASTCN) && (type != S_IFDIR) && (type != S_IFLNK)) { | |
149 | retval = ENOTDIR; | |
150 | goto exit; | |
151 | } | |
152 | /* Don't cache directory hardlink names. */ | |
153 | if (attr.ca_recflags & kHFSHasLinkChainMask) { | |
154 | cnp->cn_flags &= ~MAKEENTRY; | |
155 | } | |
156 | /* Names with composed chars are not cached. */ | |
157 | if (cnp->cn_namelen != desc.cd_namelen) | |
158 | cnp->cn_flags &= ~MAKEENTRY; | |
159 | ||
160 | retval = hfs_getnewvnode(hfsmp, dvp, cnp, &desc, 0, &attr, &fork, &tvp, &newvnode_flags); | |
161 | ||
162 | if (retval) { | |
163 | /* | |
164 | * If this was a create/rename operation lookup, then by this point | |
165 | * we expected to see the item returned from hfs_getnewvnode above. | |
166 | * In the create case, it would probably eventually bubble out an EEXIST | |
167 | * because the item existed when we were trying to create it. In the | |
168 | * rename case, it would let us know that we need to go ahead and | |
169 | * delete it as part of the rename. However, if we hit the condition below | |
170 | * then it means that we found the element during cat_lookup above, but | |
171 | * it is now no longer there. We simply behave as though we never found | |
172 | * the element at all and return EJUSTRETURN. | |
173 | */ | |
174 | if ((retval == ENOENT) && | |
175 | ((cnp->cn_nameiop == CREATE) || (cnp->cn_nameiop == RENAME)) && | |
176 | (flags & ISLASTCN)) { | |
177 | retval = EJUSTRETURN; | |
178 | } | |
179 | ||
180 | /* | |
181 | * If this was a straight lookup operation, we may need to redrive the entire | |
182 | * lookup starting from cat_lookup if the element was deleted as the result of | |
183 | * a rename operation. Since rename is supposed to guarantee atomicity, then | |
184 | * lookups cannot fail because the underlying element is deleted as a result of | |
185 | * the rename call -- either they returned the looked up element prior to rename | |
186 | * or return the newer element. If we are in this region, then all we can do is add | |
187 | * workarounds to guarantee the latter case. The element has already been deleted, so | |
188 | * we just re-try the lookup to ensure the caller gets the most recent element. | |
189 | */ | |
190 | if ((retval == ENOENT) && (cnp->cn_nameiop == LOOKUP) && | |
191 | (newvnode_flags & (GNV_CHASH_RENAMED | GNV_CAT_DELETED))) { | |
192 | if (dcp) { | |
193 | hfs_unlock (dcp); | |
194 | } | |
195 | /* get rid of any name buffers that may have lingered from the cat_lookup call */ | |
196 | cat_releasedesc (&desc); | |
197 | goto retry; | |
198 | } | |
199 | ||
200 | /* Also, re-drive the lookup if the item we looked up was a hardlink, and the number | |
201 | * or name of hardlinks has changed in the interim between the cat_lookup above, and | |
202 | * our call to hfs_getnewvnode. hfs_getnewvnode will validate the cattr we passed it | |
203 | * against what is actually in the catalog after the cnode is created. If there were | |
204 | * any issues, it will bubble out ERECYCLE, which we need to swallow and use as the | |
205 | * key to redrive as well. We need to special case this below because in this case, | |
206 | * it needs to occur regardless of the type of lookup we're doing here. | |
207 | */ | |
208 | if ((retval == ERECYCLE) && (newvnode_flags & GNV_CAT_ATTRCHANGED)) { | |
209 | if (dcp) { | |
210 | hfs_unlock (dcp); | |
211 | } | |
212 | /* get rid of any name buffers that may have lingered from the cat_lookup call */ | |
213 | cat_releasedesc (&desc); | |
214 | goto retry; | |
215 | } | |
216 | ||
217 | /* skip to the error-handling code if we can't retry */ | |
218 | goto exit; | |
219 | } | |
220 | ||
221 | /* | |
222 | * Save the origin info for file and directory hardlinks. Directory hardlinks | |
223 | * need the origin for '..' lookups, and file hardlinks need it to ensure that | |
224 | * competing lookups do not cause us to vend different hardlinks than the ones requested. | |
225 | */ | |
226 | if (ISSET(VTOC(tvp)->c_flag, C_HARDLINK)) | |
227 | hfs_savelinkorigin(VTOC(tvp), VTOC(dvp)->c_fileid); | |
228 | ||
229 | *cnode_locked = 1; | |
230 | *vpp = tvp; | |
231 | ||
232 | exit: | |
233 | if (dcp) { | |
234 | hfs_unlock(dcp); | |
235 | } | |
236 | cat_releasedesc(&desc); | |
237 | ||
238 | return (retval); | |
239 | } | |
240 | ||
241 | int | |
242 | hfs_vnop_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) | |
243 | { | |
244 | int error = 0; | |
245 | int cnode_locked = 0; | |
246 | *vpp = NULL; | |
247 | ||
248 | error = hfs_lookup(dvp, vpp, cnp, &cnode_locked); | |
249 | ||
250 | if (cnode_locked) | |
251 | hfs_unlock(VTOC(*vpp)); | |
252 | ||
253 | return (error); | |
254 | } |