]> git.saurik.com Git - apple/hfs.git/blob - livefiles_hfs_plugin/lf_hfs_lookup.c
hfs-556.100.11.tar.gz
[apple/hfs.git] / livefiles_hfs_plugin / lf_hfs_lookup.c
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 }