]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
5d5c5d0d A |
2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
3 | * | |
8f6c56a5 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
8f6c56a5 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
8ad349bb | 24 | * limitations under the License. |
8f6c56a5 A |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
1c79356b A |
27 | */ |
28 | /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ | |
29 | /* | |
30 | * Copyright (c) 1989, 1993 | |
31 | * The Regents of the University of California. All rights reserved. | |
32 | * (c) UNIX System Laboratories, Inc. | |
33 | * All or some portions of this file are derived from material licensed | |
34 | * to the University of California by American Telephone and Telegraph | |
35 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
36 | * the permission of UNIX System Laboratories, Inc. | |
37 | * | |
38 | * Redistribution and use in source and binary forms, with or without | |
39 | * modification, are permitted provided that the following conditions | |
40 | * are met: | |
41 | * 1. Redistributions of source code must retain the above copyright | |
42 | * notice, this list of conditions and the following disclaimer. | |
43 | * 2. Redistributions in binary form must reproduce the above copyright | |
44 | * notice, this list of conditions and the following disclaimer in the | |
45 | * documentation and/or other materials provided with the distribution. | |
46 | * 3. All advertising materials mentioning features or use of this software | |
47 | * must display the following acknowledgement: | |
48 | * This product includes software developed by the University of | |
49 | * California, Berkeley and its contributors. | |
50 | * 4. Neither the name of the University nor the names of its contributors | |
51 | * may be used to endorse or promote products derived from this software | |
52 | * without specific prior written permission. | |
53 | * | |
54 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
55 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
56 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
57 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
58 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
59 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
60 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
61 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
62 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
63 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
64 | * SUCH DAMAGE. | |
65 | * | |
66 | * @(#)ufs_lookup.c 8.15 (Berkeley) 6/16/95 | |
67 | */ | |
68 | #include <rev_endian_fs.h> | |
69 | #include <sys/param.h> | |
70 | #include <sys/namei.h> | |
71 | #include <sys/buf.h> | |
72 | #include <sys/file.h> | |
91447636 A |
73 | #include <sys/mount_internal.h> |
74 | #include <sys/vnode_internal.h> | |
9bccf70c | 75 | #include <sys/quota.h> |
91447636 A |
76 | #include <sys/kauth.h> |
77 | #include <sys/uio_internal.h> | |
1c79356b A |
78 | |
79 | #include <ufs/ufs/quota.h> | |
80 | #include <ufs/ufs/inode.h> | |
81 | #include <ufs/ufs/dir.h> | |
82 | #include <ufs/ufs/ufsmount.h> | |
83 | #include <ufs/ufs/ufs_extern.h> | |
91447636 | 84 | #include <ufs/ffs/ffs_extern.h> |
1c79356b A |
85 | #if REV_ENDIAN_FS |
86 | #include <ufs/ufs/ufs_byte_order.h> | |
1c79356b A |
87 | #endif /* REV_ENDIAN_FS */ |
88 | ||
89b3af67 | 89 | struct nchstats ufs_nchstats; |
1c79356b A |
90 | #if DIAGNOSTIC |
91 | int dirchk = 1; | |
92 | #else | |
93 | int dirchk = 0; | |
94 | #endif | |
95 | ||
96 | #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) | |
97 | ||
98 | /* | |
99 | * Convert a component of a pathname into a pointer to a locked inode. | |
100 | * This is a very central and rather complicated routine. | |
101 | * If the file system is not maintained in a strict tree hierarchy, | |
102 | * this can result in a deadlock situation (see comments in code below). | |
103 | * | |
104 | * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending | |
105 | * on whether the name is to be looked up, created, renamed, or deleted. | |
106 | * When CREATE, RENAME, or DELETE is specified, information usable in | |
107 | * creating, renaming, or deleting a directory entry may be calculated. | |
108 | * If flag has LOCKPARENT or'ed into it and the target of the pathname | |
109 | * exists, lookup returns both the target and its parent directory locked. | |
110 | * When creating or renaming and LOCKPARENT is specified, the target may | |
111 | * not be ".". When deleting and LOCKPARENT is specified, the target may | |
91447636 | 112 | * be "."., |
1c79356b A |
113 | * |
114 | * Overall outline of ufs_lookup: | |
115 | * | |
116 | * check accessibility of directory | |
117 | * look for name in cache, if found, then if at end of path | |
118 | * and deleting or creating, drop it, else return name | |
119 | * search for name in directory, to found or notfound | |
120 | * notfound: | |
121 | * if creating, return locked directory, leaving info on available slots | |
122 | * else return error | |
123 | * found: | |
124 | * if at end of path and deleting, return information to allow delete | |
125 | * if at end of path and rewriting (RENAME and LOCKPARENT), lock target | |
126 | * inode and return info to allow rewrite | |
127 | * if not at end, add name to cache; if at end and neither creating | |
128 | * nor deleting, add name to cache | |
129 | */ | |
130 | int | |
131 | ufs_lookup(ap) | |
91447636 | 132 | struct vnop_lookup_args /* { |
1c79356b A |
133 | struct vnode *a_dvp; |
134 | struct vnode **a_vpp; | |
135 | struct componentname *a_cnp; | |
91447636 | 136 | vfs_context_t a_context |
1c79356b A |
137 | } */ *ap; |
138 | { | |
139 | register struct vnode *vdp; /* vnode for directory being searched */ | |
140 | register struct inode *dp; /* inode for directory being searched */ | |
141 | struct buf *bp; /* a buffer of directory entries */ | |
142 | register struct direct *ep; /* the current directory entry */ | |
143 | int entryoffsetinblock; /* offset of ep in bp's buffer */ | |
144 | enum {NONE, COMPACT, FOUND} slotstatus; | |
145 | doff_t slotoffset; /* offset of area with free space */ | |
146 | int slotsize; /* size of area at slotoffset */ | |
147 | int slotfreespace; /* amount of space free in slot */ | |
148 | int slotneeded; /* size of the entry we're seeking */ | |
149 | int numdirpasses; /* strategy for directory search */ | |
150 | doff_t endsearch; /* offset to end directory search */ | |
151 | doff_t prevoff; /* prev entry dp->i_offset */ | |
152 | struct vnode *pdp; /* saved dp during symlink work */ | |
153 | struct vnode *tdp; /* returned by VFS_VGET */ | |
154 | doff_t enduseful; /* pointer past last used dir slot */ | |
155 | u_long bmask; /* block offset mask */ | |
1c79356b A |
156 | int wantparent; /* 1 => wantparent or lockparent flag */ |
157 | int namlen, error; | |
158 | struct vnode **vpp = ap->a_vpp; | |
159 | struct componentname *cnp = ap->a_cnp; | |
1c79356b A |
160 | int flags = cnp->cn_flags; |
161 | int nameiop = cnp->cn_nameiop; | |
91447636 A |
162 | vfs_context_t context = ap->a_context; |
163 | kauth_cred_t cred; | |
1c79356b A |
164 | #if REV_ENDIAN_FS |
165 | int rev_endian=0; | |
166 | #endif /* REV_ENDIAN_FS */ | |
167 | ||
168 | ||
8f6c56a5 A |
169 | if (cnp->cn_namelen > MAXNAMLEN) |
170 | return (ENAMETOOLONG); | |
171 | ||
91447636 | 172 | cred = vfs_context_ucred(context); |
1c79356b A |
173 | bp = NULL; |
174 | slotoffset = -1; | |
175 | *vpp = NULL; | |
176 | vdp = ap->a_dvp; | |
177 | dp = VTOI(vdp); | |
91447636 | 178 | |
1c79356b | 179 | wantparent = flags & (LOCKPARENT|WANTPARENT); |
91447636 | 180 | |
1c79356b A |
181 | #if REV_ENDIAN_FS |
182 | rev_endian=(vdp->v_mount->mnt_flag & MNT_REVEND); | |
183 | #endif /* REV_ENDIAN_FS */ | |
184 | ||
185 | /* | |
186 | * Check accessiblity of directory. | |
187 | */ | |
188 | if ((dp->i_mode & IFMT) != IFDIR) | |
189 | return (ENOTDIR); | |
1c79356b A |
190 | |
191 | /* | |
192 | * We now have a segment name to search for, and a directory to search. | |
193 | * | |
194 | * Before tediously performing a linear scan of the directory, | |
195 | * check the name cache to see if the directory/name pair | |
196 | * we are looking for is known already. | |
197 | */ | |
198 | if (error = cache_lookup(vdp, vpp, cnp)) { | |
1c79356b A |
199 | if (error == ENOENT) |
200 | return (error); | |
91447636 | 201 | return (0); |
1c79356b | 202 | } |
1c79356b A |
203 | /* |
204 | * Suppress search for slots unless creating | |
205 | * file and at end of pathname, in which case | |
206 | * we watch for a place to put the new file in | |
207 | * case it doesn't already exist. | |
208 | */ | |
209 | slotstatus = FOUND; | |
210 | slotfreespace = slotsize = slotneeded = 0; | |
211 | if ((nameiop == CREATE || nameiop == RENAME) && | |
212 | (flags & ISLASTCN)) { | |
213 | slotstatus = NONE; | |
214 | slotneeded = (sizeof(struct direct) - MAXNAMLEN + | |
215 | cnp->cn_namelen + 3) &~ 3; | |
216 | } | |
1c79356b A |
217 | /* |
218 | * If there is cached information on a previous search of | |
219 | * this directory, pick up where we last left off. | |
220 | * We cache only lookups as these are the most common | |
221 | * and have the greatest payoff. Caching CREATE has little | |
222 | * benefit as it usually must search the entire directory | |
223 | * to determine that the entry does not exist. Caching the | |
224 | * location of the last DELETE or RENAME has not reduced | |
225 | * profiling time and hence has been removed in the interest | |
226 | * of simplicity. | |
227 | */ | |
91447636 | 228 | bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_vfsstat.f_iosize - 1; |
1c79356b A |
229 | if (nameiop != LOOKUP || dp->i_diroff == 0 || |
230 | dp->i_diroff > dp->i_size) { | |
231 | entryoffsetinblock = 0; | |
232 | dp->i_offset = 0; | |
233 | numdirpasses = 1; | |
234 | } else { | |
235 | dp->i_offset = dp->i_diroff; | |
236 | if ((entryoffsetinblock = dp->i_offset & bmask) && | |
91447636 A |
237 | (error = ffs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp))) |
238 | goto out; | |
1c79356b | 239 | numdirpasses = 2; |
89b3af67 | 240 | ufs_nchstats.ncs_2passes++; |
1c79356b A |
241 | } |
242 | prevoff = dp->i_offset; | |
243 | endsearch = roundup(dp->i_size, DIRBLKSIZ); | |
244 | enduseful = 0; | |
245 | ||
246 | searchloop: | |
247 | while (dp->i_offset < endsearch) { | |
248 | /* | |
249 | * If necessary, get the next directory block. | |
250 | */ | |
251 | if ((dp->i_offset & bmask) == 0) { | |
252 | if (bp != NULL) { | |
253 | #if REV_ENDIAN_FS | |
254 | if (rev_endian) | |
255 | byte_swap_dir_block_out(bp); | |
256 | #endif /* REV_ENDIAN_FS */ | |
91447636 | 257 | buf_brelse(bp); |
1c79356b | 258 | } |
91447636 A |
259 | if (error = ffs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp)) |
260 | goto out; | |
1c79356b A |
261 | entryoffsetinblock = 0; |
262 | } | |
263 | /* | |
264 | * If still looking for a slot, and at a DIRBLKSIZE | |
265 | * boundary, have to start looking for free space again. | |
266 | */ | |
267 | if (slotstatus == NONE && | |
268 | (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { | |
269 | slotoffset = -1; | |
270 | slotfreespace = 0; | |
271 | } | |
272 | /* | |
273 | * Get pointer to next entry. | |
8f6c56a5 | 274 | * Full validation checks are slow, but necessary. |
1c79356b | 275 | */ |
91447636 | 276 | ep = (struct direct *)((char *)buf_dataptr(bp) + entryoffsetinblock); |
1c79356b | 277 | if (ep->d_reclen == 0 || |
8f6c56a5 | 278 | ufs_dirbadentry(vdp, ep, entryoffsetinblock)) { |
1c79356b A |
279 | int i; |
280 | ||
281 | ufs_dirbad(dp, dp->i_offset, "mangled entry"); | |
282 | i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); | |
283 | dp->i_offset += i; | |
284 | entryoffsetinblock += i; | |
285 | continue; | |
286 | } | |
287 | ||
288 | /* | |
289 | * If an appropriate sized slot has not yet been found, | |
290 | * check to see if one is available. Also accumulate space | |
291 | * in the current block so that we can determine if | |
292 | * compaction is viable. | |
293 | */ | |
294 | if (slotstatus != FOUND) { | |
295 | int size = ep->d_reclen; | |
296 | ||
297 | if (ep->d_ino != 0) | |
298 | size -= DIRSIZ(FSFMT(vdp), ep); | |
299 | if (size > 0) { | |
300 | if (size >= slotneeded) { | |
301 | slotstatus = FOUND; | |
302 | slotoffset = dp->i_offset; | |
303 | slotsize = ep->d_reclen; | |
304 | } else if (slotstatus == NONE) { | |
305 | slotfreespace += size; | |
306 | if (slotoffset == -1) | |
307 | slotoffset = dp->i_offset; | |
308 | if (slotfreespace >= slotneeded) { | |
309 | slotstatus = COMPACT; | |
310 | slotsize = dp->i_offset + | |
311 | ep->d_reclen - slotoffset; | |
312 | } | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | /* | |
318 | * Check for a name match. | |
319 | */ | |
320 | if (ep->d_ino) { | |
321 | # if (BYTE_ORDER == LITTLE_ENDIAN) | |
322 | if (vdp->v_mount->mnt_maxsymlinklen > 0) | |
323 | namlen = ep->d_namlen; | |
324 | else | |
325 | namlen = ep->d_type; | |
326 | # else | |
327 | namlen = ep->d_namlen; | |
328 | # endif | |
329 | if (namlen == cnp->cn_namelen && | |
330 | !bcmp(cnp->cn_nameptr, ep->d_name, | |
331 | (unsigned)namlen)) { | |
332 | /* | |
333 | * Save directory entry's inode number and | |
334 | * reclen in ndp->ni_ufs area, and release | |
335 | * directory buffer. | |
336 | */ | |
337 | if (vdp->v_mount->mnt_maxsymlinklen > 0 && | |
338 | ep->d_type == DT_WHT) { | |
339 | slotstatus = FOUND; | |
340 | slotoffset = dp->i_offset; | |
341 | slotsize = ep->d_reclen; | |
342 | dp->i_reclen = slotsize; | |
343 | enduseful = dp->i_size; | |
344 | ap->a_cnp->cn_flags |= ISWHITEOUT; | |
345 | numdirpasses--; | |
346 | goto notfound; | |
347 | } | |
348 | dp->i_ino = ep->d_ino; | |
349 | dp->i_reclen = ep->d_reclen; | |
350 | #if REV_ENDIAN_FS | |
351 | if (rev_endian) | |
352 | byte_swap_dir_block_out(bp); | |
353 | #endif /* REV_ENDIAN_FS */ | |
91447636 | 354 | buf_brelse(bp); |
1c79356b A |
355 | goto found; |
356 | } | |
357 | } | |
358 | prevoff = dp->i_offset; | |
359 | dp->i_offset += ep->d_reclen; | |
360 | entryoffsetinblock += ep->d_reclen; | |
361 | if (ep->d_ino) | |
362 | enduseful = dp->i_offset; | |
363 | } | |
364 | notfound: | |
365 | /* | |
366 | * If we started in the middle of the directory and failed | |
367 | * to find our target, we must check the beginning as well. | |
368 | */ | |
369 | if (numdirpasses == 2) { | |
370 | numdirpasses--; | |
371 | dp->i_offset = 0; | |
372 | endsearch = dp->i_diroff; | |
373 | goto searchloop; | |
374 | } | |
375 | if (bp != NULL) { | |
376 | #if REV_ENDIAN_FS | |
377 | if (rev_endian) | |
378 | byte_swap_dir_block_out(bp); | |
379 | #endif /* REV_ENDIAN_FS */ | |
91447636 | 380 | buf_brelse(bp); |
1c79356b A |
381 | } |
382 | /* | |
383 | * If creating, and at end of pathname and current | |
384 | * directory has not been removed, then can consider | |
385 | * allowing file to be created. | |
386 | */ | |
387 | if ((nameiop == CREATE || nameiop == RENAME || | |
388 | (nameiop == DELETE && | |
389 | (ap->a_cnp->cn_flags & DOWHITEOUT) && | |
390 | (ap->a_cnp->cn_flags & ISWHITEOUT))) && | |
391 | (flags & ISLASTCN) && dp->i_nlink != 0) { | |
1c79356b A |
392 | /* |
393 | * Return an indication of where the new directory | |
394 | * entry should be put. If we didn't find a slot, | |
395 | * then set dp->i_count to 0 indicating | |
396 | * that the new slot belongs at the end of the | |
397 | * directory. If we found a slot, then the new entry | |
398 | * can be put in the range from dp->i_offset to | |
399 | * dp->i_offset + dp->i_count. | |
400 | */ | |
401 | if (slotstatus == NONE) { | |
402 | dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); | |
403 | dp->i_count = 0; | |
404 | enduseful = dp->i_offset; | |
405 | } else if (nameiop == DELETE) { | |
406 | dp->i_offset = slotoffset; | |
407 | if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) | |
408 | dp->i_count = 0; | |
409 | else | |
410 | dp->i_count = dp->i_offset - prevoff; | |
411 | } else { | |
412 | dp->i_offset = slotoffset; | |
413 | dp->i_count = slotsize; | |
414 | if (enduseful < slotoffset + slotsize) | |
415 | enduseful = slotoffset + slotsize; | |
416 | } | |
417 | dp->i_endoff = roundup(enduseful, DIRBLKSIZ); | |
418 | dp->i_flag |= IN_CHANGE | IN_UPDATE; | |
419 | /* | |
420 | * We return with the directory locked, so that | |
421 | * the parameters we set up above will still be | |
422 | * valid if we actually decide to do a direnter(). | |
423 | * We return ni_vp == NULL to indicate that the entry | |
424 | * does not currently exist; we leave a pointer to | |
425 | * the (locked) directory inode in ndp->ni_dvp. | |
1c79356b A |
426 | * |
427 | * NB - if the directory is unlocked, then this | |
428 | * information cannot be used. | |
429 | */ | |
91447636 A |
430 | error = EJUSTRETURN; |
431 | goto out; | |
1c79356b A |
432 | } |
433 | /* | |
434 | * Insert name into cache (as non-existent) if appropriate. | |
435 | */ | |
436 | if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) | |
437 | cache_enter(vdp, *vpp, cnp); | |
91447636 A |
438 | error = ENOENT; |
439 | goto out; | |
1c79356b A |
440 | |
441 | found: | |
442 | if (numdirpasses == 2) | |
89b3af67 | 443 | ufs_nchstats.ncs_pass2++; |
1c79356b A |
444 | /* |
445 | * Check that directory length properly reflects presence | |
446 | * of this entry. | |
447 | */ | |
448 | if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) { | |
449 | ufs_dirbad(dp, dp->i_offset, "i_size too small"); | |
450 | dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep); | |
451 | dp->i_flag |= IN_CHANGE | IN_UPDATE; | |
452 | } | |
453 | ||
454 | /* | |
455 | * Found component in pathname. | |
456 | * If the final component of path name, save information | |
457 | * in the cache as to where the entry was found. | |
458 | */ | |
459 | if ((flags & ISLASTCN) && nameiop == LOOKUP) | |
460 | dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); | |
461 | ||
462 | /* | |
463 | * If deleting, and at end of pathname, return | |
464 | * parameters which can be used to remove file. | |
465 | * If the wantparent flag isn't set, we return only | |
466 | * the directory (in ndp->ni_dvp), otherwise we go | |
467 | * on and lock the inode, being careful with ".". | |
468 | */ | |
469 | if (nameiop == DELETE && (flags & ISLASTCN)) { | |
1c79356b A |
470 | /* |
471 | * Return pointer to current entry in dp->i_offset, | |
472 | * and distance past previous entry (if there | |
473 | * is a previous entry in this block) in dp->i_count. | |
474 | * Save directory inode pointer in ndp->ni_dvp for dirremove(). | |
475 | */ | |
476 | if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) | |
477 | dp->i_count = 0; | |
478 | else | |
479 | dp->i_count = dp->i_offset - prevoff; | |
480 | if (dp->i_number == dp->i_ino) { | |
91447636 | 481 | vnode_get(vdp); |
1c79356b | 482 | *vpp = vdp; |
91447636 A |
483 | error = 0; |
484 | goto out; | |
1c79356b | 485 | } |
91447636 A |
486 | if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0)) |
487 | goto out; | |
1c79356b | 488 | *vpp = tdp; |
91447636 | 489 | goto out; |
1c79356b A |
490 | } |
491 | ||
492 | /* | |
493 | * If rewriting (RENAME), return the inode and the | |
494 | * information required to rewrite the present directory | |
495 | * Must get inode of directory entry to verify it's a | |
496 | * regular file, or empty directory. | |
497 | */ | |
498 | if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { | |
1c79356b A |
499 | /* |
500 | * Careful about locking second inode. | |
501 | * This can only occur if the target is ".". | |
502 | */ | |
91447636 A |
503 | if (dp->i_number == dp->i_ino) { |
504 | error =EISDIR; | |
505 | goto out; | |
506 | } | |
507 | if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0)) | |
508 | goto out; | |
1c79356b | 509 | *vpp = tdp; |
91447636 A |
510 | |
511 | goto out; | |
1c79356b A |
512 | } |
513 | ||
514 | /* | |
91447636 | 515 | * Step through the translation in the name. We do not `vnode_put' the |
1c79356b A |
516 | * directory because we may need it again if a symbolic link |
517 | * is relative to the current directory. Instead we save it | |
518 | * unlocked as "pdp". We must get the target inode before unlocking | |
519 | * the directory to insure that the inode will not be removed | |
520 | * before we get it. We prevent deadlock by always fetching | |
521 | * inodes from the root, moving down the directory tree. Thus | |
522 | * when following backward pointers ".." we must unlock the | |
523 | * parent directory before getting the requested directory. | |
524 | * There is a potential race condition here if both the current | |
525 | * and parent directories are removed before the VFS_VGET for the | |
526 | * inode associated with ".." returns. We hope that this occurs | |
527 | * infrequently since we cannot avoid this race condition without | |
528 | * implementing a sophisticated deadlock detection algorithm. | |
529 | * Note also that this simple deadlock detection scheme will not | |
530 | * work if the file system has any hard links other than ".." | |
531 | * that point backwards in the directory structure. | |
532 | */ | |
533 | pdp = vdp; | |
534 | if (flags & ISDOTDOT) { | |
91447636 A |
535 | if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0)) { |
536 | goto out; | |
1c79356b A |
537 | } |
538 | *vpp = tdp; | |
539 | } else if (dp->i_number == dp->i_ino) { | |
91447636 | 540 | vnode_get(vdp); /* we want ourself, ie "." */ |
1c79356b A |
541 | *vpp = vdp; |
542 | } else { | |
91447636 A |
543 | if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0)) |
544 | goto out; | |
1c79356b A |
545 | *vpp = tdp; |
546 | } | |
547 | ||
91447636 A |
548 | error = 0; |
549 | out: | |
550 | return (error); | |
1c79356b A |
551 | } |
552 | ||
553 | void | |
554 | ufs_dirbad(ip, offset, how) | |
555 | struct inode *ip; | |
556 | doff_t offset; | |
91447636 | 557 | const char *how; |
1c79356b A |
558 | { |
559 | struct mount *mp; | |
560 | ||
561 | mp = ITOV(ip)->v_mount; | |
562 | (void)printf("%s: bad dir ino %d at offset %d: %s\n", | |
91447636 | 563 | mp->mnt_vfsstat.f_mntonname, ip->i_number, offset, how); |
8f6c56a5 | 564 | #if 0 |
91447636 | 565 | if ((mp->mnt_vfsstat.f_flags & MNT_RDONLY) == 0) |
1c79356b | 566 | panic("bad dir"); |
8f6c56a5 | 567 | #endif |
1c79356b A |
568 | } |
569 | ||
570 | /* | |
571 | * Do consistency checking on a directory entry: | |
572 | * record length must be multiple of 4 | |
573 | * entry must fit in rest of its DIRBLKSIZ block | |
574 | * record must be large enough to contain entry | |
575 | * name is not longer than MAXNAMLEN | |
576 | * name must be as long as advertised, and null terminated | |
577 | */ | |
578 | int | |
579 | ufs_dirbadentry(dp, ep, entryoffsetinblock) | |
580 | struct vnode *dp; | |
581 | register struct direct *ep; | |
582 | int entryoffsetinblock; | |
583 | { | |
584 | register int i; | |
585 | int namlen; | |
8f6c56a5 A |
586 | ino_t maxino = 0; |
587 | struct fs *fs; | |
588 | struct ufsmount *ump = VFSTOUFS(dp->v_mount); | |
1c79356b A |
589 | |
590 | # if (BYTE_ORDER == LITTLE_ENDIAN) | |
591 | if (dp->v_mount->mnt_maxsymlinklen > 0) | |
592 | namlen = ep->d_namlen; | |
593 | else | |
594 | namlen = ep->d_type; | |
595 | # else | |
596 | namlen = ep->d_namlen; | |
597 | # endif | |
598 | if ((ep->d_reclen & 0x3) != 0 || | |
599 | ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || | |
600 | ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) { | |
601 | /*return (1); */ | |
602 | printf("First bad\n"); | |
603 | goto bad; | |
604 | } | |
605 | if (ep->d_ino == 0) | |
606 | return (0); | |
607 | for (i = 0; i < namlen; i++) | |
608 | if (ep->d_name[i] == '\0') { | |
609 | /*return (1); */ | |
610 | printf("Second bad\n"); | |
611 | goto bad; | |
612 | } | |
613 | if (ep->d_name[i]) | |
614 | goto bad; | |
8f6c56a5 A |
615 | |
616 | fs = ump->um_fs; | |
617 | maxino = fs->fs_ncg * fs->fs_ipg; | |
618 | if (ep->d_ino > maxino) { | |
619 | printf("Third bad\n"); | |
620 | goto bad; | |
621 | } | |
622 | ||
1c79356b A |
623 | return (0); |
624 | bad: | |
625 | return (1); | |
626 | } | |
627 | ||
628 | /* | |
629 | * Write a directory entry after a call to namei, using the parameters | |
630 | * that it left in nameidata. The argument ip is the inode which the new | |
631 | * directory entry will refer to. Dvp is a pointer to the directory to | |
632 | * be written, which was left locked by namei. Remaining parameters | |
633 | * (dp->i_offset, dp->i_count) indicate how the space for the new | |
634 | * entry is to be obtained. | |
635 | */ | |
636 | int | |
637 | ufs_direnter(ip, dvp, cnp) | |
638 | struct inode *ip; | |
639 | struct vnode *dvp; | |
640 | register struct componentname *cnp; | |
641 | { | |
642 | register struct inode *dp; | |
643 | struct direct newdir; | |
644 | ||
1c79356b A |
645 | dp = VTOI(dvp); |
646 | newdir.d_ino = ip->i_number; | |
647 | newdir.d_namlen = cnp->cn_namelen; | |
648 | bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1); | |
649 | if (dvp->v_mount->mnt_maxsymlinklen > 0) | |
650 | newdir.d_type = IFTODT(ip->i_mode); | |
651 | else { | |
652 | newdir.d_type = 0; | |
653 | # if (BYTE_ORDER == LITTLE_ENDIAN) | |
654 | { u_char tmp = newdir.d_namlen; | |
655 | newdir.d_namlen = newdir.d_type; | |
656 | newdir.d_type = tmp; } | |
657 | # endif | |
658 | } | |
91447636 | 659 | return (ufs_direnter2(dvp, &newdir, cnp->cn_context)); |
1c79356b A |
660 | } |
661 | ||
662 | /* | |
663 | * Common entry point for directory entry removal used by ufs_direnter | |
664 | * and ufs_whiteout | |
665 | */ | |
91447636 A |
666 | int |
667 | ufs_direnter2(struct vnode *dvp, struct direct *dirp, vfs_context_t ctx) | |
1c79356b A |
668 | { |
669 | int newentrysize; | |
670 | struct inode *dp; | |
671 | struct buf *bp; | |
91447636 | 672 | uio_t auio; |
1c79356b A |
673 | u_int dsize; |
674 | struct direct *ep, *nep; | |
675 | int error, loc, spacefree; | |
676 | char *dirbuf; | |
91447636 | 677 | char uio_buf[ UIO_SIZEOF(1) ]; |
1c79356b A |
678 | #if REV_ENDIAN_FS |
679 | struct mount *mp=dvp->v_mount; | |
680 | int rev_endian=(mp->mnt_flag & MNT_REVEND); | |
681 | #endif /* REV_ENDIAN_FS */ | |
682 | ||
683 | dp = VTOI(dvp); | |
684 | newentrysize = DIRSIZ(FSFMT(dvp), dirp); | |
685 | ||
686 | if (dp->i_count == 0) { | |
687 | /* | |
688 | * If dp->i_count is 0, then namei could find no | |
689 | * space in the directory. Here, dp->i_offset will | |
690 | * be on a directory block boundary and we will write the | |
691 | * new entry into a fresh block. | |
692 | */ | |
693 | if (dp->i_offset & (DIRBLKSIZ - 1)) | |
694 | panic("ufs_direnter2: newblk"); | |
1c79356b | 695 | dirp->d_reclen = DIRBLKSIZ; |
91447636 A |
696 | auio = uio_createwithbuffer(1, dp->i_offset, UIO_SYSSPACE, UIO_WRITE, |
697 | &uio_buf[0], sizeof(uio_buf)); | |
698 | uio_addiov(auio, CAST_USER_ADDR_T(dirp), newentrysize); | |
699 | ||
700 | error = ffs_write_internal(dvp, auio, IO_SYNC, vfs_context_ucred(ctx)); | |
1c79356b | 701 | if (DIRBLKSIZ > |
91447636 | 702 | VFSTOUFS(dvp->v_mount)->um_mountp->mnt_vfsstat.f_bsize) |
1c79356b A |
703 | /* XXX should grow with balloc() */ |
704 | panic("ufs_direnter2: frag size"); | |
705 | else if (!error) { | |
706 | dp->i_size = roundup(dp->i_size, DIRBLKSIZ); | |
707 | dp->i_flag |= IN_CHANGE; | |
708 | } | |
709 | return (error); | |
710 | } | |
711 | ||
712 | /* | |
713 | * If dp->i_count is non-zero, then namei found space | |
714 | * for the new entry in the range dp->i_offset to | |
715 | * dp->i_offset + dp->i_count in the directory. | |
716 | * To use this space, we may have to compact the entries located | |
717 | * there, by copying them together towards the beginning of the | |
718 | * block, leaving the free space in one usable chunk at the end. | |
719 | */ | |
720 | ||
721 | /* | |
722 | * Increase size of directory if entry eats into new space. | |
723 | * This should never push the size past a new multiple of | |
724 | * DIRBLKSIZE. | |
725 | * | |
726 | * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. | |
727 | */ | |
728 | if (dp->i_offset + dp->i_count > dp->i_size) | |
729 | dp->i_size = dp->i_offset + dp->i_count; | |
730 | /* | |
731 | * Get the block containing the space for the new directory entry. | |
732 | */ | |
91447636 | 733 | if (error = ffs_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) |
1c79356b A |
734 | return (error); |
735 | /* | |
736 | * Find space for the new entry. In the simple case, the entry at | |
737 | * offset base will have the space. If it does not, then namei | |
738 | * arranged that compacting the region dp->i_offset to | |
739 | * dp->i_offset + dp->i_count would yield the | |
740 | * space. | |
741 | */ | |
742 | ep = (struct direct *)dirbuf; | |
743 | dsize = DIRSIZ(FSFMT(dvp), ep); | |
744 | spacefree = ep->d_reclen - dsize; | |
745 | for (loc = ep->d_reclen; loc < dp->i_count; ) { | |
746 | nep = (struct direct *)(dirbuf + loc); | |
747 | if (ep->d_ino) { | |
748 | /* trim the existing slot */ | |
749 | ep->d_reclen = dsize; | |
750 | ep = (struct direct *)((char *)ep + dsize); | |
751 | } else { | |
752 | /* overwrite; nothing there; header is ours */ | |
753 | spacefree += dsize; | |
754 | } | |
755 | dsize = DIRSIZ(FSFMT(dvp), nep); | |
756 | spacefree += nep->d_reclen - dsize; | |
757 | loc += nep->d_reclen; | |
758 | bcopy((caddr_t)nep, (caddr_t)ep, dsize); | |
759 | } | |
760 | /* | |
761 | * Update the pointer fields in the previous entry (if any), | |
762 | * copy in the new entry, and write out the block. | |
763 | */ | |
764 | if (ep->d_ino == 0 || | |
765 | (ep->d_ino == WINO && | |
766 | bcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) { | |
767 | if (spacefree + dsize < newentrysize) | |
768 | panic("ufs_direnter2: compact1"); | |
769 | dirp->d_reclen = spacefree + dsize; | |
770 | } else { | |
771 | if (spacefree < newentrysize) | |
772 | panic("ufs_direnter2: compact2"); | |
773 | dirp->d_reclen = spacefree; | |
774 | ep->d_reclen = dsize; | |
775 | ep = (struct direct *)((char *)ep + dsize); | |
776 | } | |
777 | bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize); | |
778 | #if REV_ENDIAN_FS | |
779 | if (rev_endian) | |
780 | byte_swap_dir_block_out(bp); | |
781 | #endif /* REV_ENDIAN_FS */ | |
55e303ae A |
782 | if (mp->mnt_flag & MNT_ASYNC) { |
783 | error = 0; | |
91447636 | 784 | buf_bdwrite(bp); |
55e303ae | 785 | } else { |
91447636 | 786 | error = VNOP_BWRITE(bp); |
55e303ae | 787 | } |
1c79356b A |
788 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
789 | if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) | |
91447636 A |
790 | error = ffs_truncate_internal(dvp, (off_t)dp->i_endoff, IO_SYNC, vfs_context_ucred(ctx)); |
791 | ||
1c79356b A |
792 | return (error); |
793 | } | |
794 | ||
795 | /* | |
796 | * Remove a directory entry after a call to namei, using | |
797 | * the parameters which it left in nameidata. The entry | |
798 | * dp->i_offset contains the offset into the directory of the | |
799 | * entry to be eliminated. The dp->i_count field contains the | |
800 | * size of the previous record in the directory. If this | |
801 | * is 0, the first entry is being deleted, so we need only | |
802 | * zero the inode number to mark the entry as free. If the | |
803 | * entry is not the first in the directory, we must reclaim | |
804 | * the space of the now empty record by adding the record size | |
805 | * to the size of the previous entry. | |
806 | */ | |
807 | int | |
808 | ufs_dirremove(dvp, cnp) | |
809 | struct vnode *dvp; | |
810 | struct componentname *cnp; | |
811 | { | |
812 | register struct inode *dp; | |
813 | struct direct *ep; | |
814 | struct buf *bp; | |
815 | int error; | |
816 | #if REV_ENDIAN_FS | |
817 | struct mount *mp=dvp->v_mount; | |
818 | int rev_endian=(mp->mnt_flag & MNT_REVEND); | |
819 | #endif /* REV_ENDIAN_FS */ | |
820 | ||
821 | dp = VTOI(dvp); | |
822 | ||
823 | if (cnp->cn_flags & DOWHITEOUT) { | |
824 | /* | |
825 | * Whiteout entry: set d_ino to WINO. | |
826 | */ | |
91447636 | 827 | if (error = ffs_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) |
1c79356b A |
828 | return (error); |
829 | ep->d_ino = WINO; | |
830 | ep->d_type = DT_WHT; | |
831 | #if REV_ENDIAN_FS | |
832 | if (rev_endian) | |
833 | byte_swap_dir_block_out(bp); | |
834 | #endif /* REV_ENDIAN_FS */ | |
55e303ae A |
835 | if (mp->mnt_flag & MNT_ASYNC) { |
836 | error = 0; | |
91447636 | 837 | buf_bdwrite(bp); |
55e303ae | 838 | } else { |
91447636 | 839 | error = VNOP_BWRITE(bp); |
55e303ae | 840 | } |
1c79356b A |
841 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
842 | return (error); | |
843 | } | |
844 | ||
845 | if (dp->i_count == 0) { | |
846 | /* | |
847 | * First entry in block: set d_ino to zero. | |
848 | */ | |
91447636 | 849 | if (error = ffs_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) |
1c79356b A |
850 | return (error); |
851 | ep->d_ino = 0; | |
852 | #if REV_ENDIAN_FS | |
853 | if (rev_endian) | |
854 | byte_swap_dir_block_out(bp); | |
855 | #endif /* REV_ENDIAN_FS */ | |
55e303ae A |
856 | if (mp->mnt_flag & MNT_ASYNC) { |
857 | error = 0; | |
91447636 | 858 | buf_bdwrite(bp); |
55e303ae | 859 | } else { |
91447636 | 860 | error = VNOP_BWRITE(bp); |
55e303ae | 861 | } |
1c79356b A |
862 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
863 | return (error); | |
864 | } | |
865 | /* | |
866 | * Collapse new free space into previous entry. | |
867 | */ | |
91447636 | 868 | if (error = ffs_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), |
1c79356b A |
869 | (char **)&ep, &bp)) |
870 | return (error); | |
871 | ep->d_reclen += dp->i_reclen; | |
872 | #if REV_ENDIAN_FS | |
873 | if (rev_endian) | |
874 | byte_swap_dir_block_out(bp); | |
875 | #endif /* REV_ENDIAN_FS */ | |
55e303ae A |
876 | if (mp->mnt_flag & MNT_ASYNC) { |
877 | error = 0; | |
91447636 | 878 | buf_bdwrite(bp); |
55e303ae | 879 | } else { |
91447636 | 880 | error = VNOP_BWRITE(bp); |
55e303ae | 881 | } |
1c79356b | 882 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
91447636 | 883 | |
1c79356b A |
884 | return (error); |
885 | } | |
886 | ||
887 | /* | |
888 | * Rewrite an existing directory entry to point at the inode | |
889 | * supplied. The parameters describing the directory entry are | |
890 | * set up by a call to namei. | |
891 | */ | |
892 | int | |
893 | ufs_dirrewrite(dp, ip, cnp) | |
894 | struct inode *dp, *ip; | |
895 | struct componentname *cnp; | |
896 | { | |
897 | struct buf *bp; | |
898 | struct direct *ep; | |
899 | struct vnode *vdp = ITOV(dp); | |
900 | int error; | |
901 | ||
91447636 | 902 | if (error = ffs_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) |
1c79356b A |
903 | return (error); |
904 | ep->d_ino = ip->i_number; | |
905 | if (vdp->v_mount->mnt_maxsymlinklen > 0) | |
906 | ep->d_type = IFTODT(ip->i_mode); | |
907 | #if REV_ENDIAN_FS | |
908 | if (vdp->v_mount->mnt_flag & MNT_REVEND) | |
909 | byte_swap_dir_block_out(bp); | |
910 | #endif /* REV_ENDIAN_FS */ | |
55e303ae A |
911 | if (vdp->v_mount->mnt_flag & MNT_ASYNC) { |
912 | error = 0; | |
91447636 | 913 | buf_bdwrite(bp); |
55e303ae | 914 | } else { |
91447636 | 915 | error = VNOP_BWRITE(bp); |
55e303ae | 916 | } |
1c79356b A |
917 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
918 | return (error); | |
919 | } | |
920 | ||
921 | /* | |
922 | * Check if a directory is empty or not. | |
923 | * Inode supplied must be locked. | |
924 | * | |
925 | * Using a struct dirtemplate here is not precisely | |
926 | * what we want, but better than using a struct direct. | |
927 | * | |
928 | * NB: does not handle corrupted directories. | |
929 | */ | |
930 | int | |
91447636 | 931 | ufs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred) |
1c79356b A |
932 | { |
933 | register off_t off; | |
934 | struct dirtemplate dbuf; | |
935 | register struct direct *dp = (struct direct *)&dbuf; | |
936 | int error, count, namlen; | |
937 | #if REV_ENDIAN_FS | |
938 | struct vnode *vp=ITOV(ip); | |
939 | struct mount *mp=vp->v_mount; | |
940 | int rev_endian=(mp->mnt_flag & MNT_REVEND); | |
941 | #endif /* REV_ENDIAN_FS */ | |
942 | ||
943 | #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) | |
944 | ||
945 | for (off = 0; off < ip->i_size; off += dp->d_reclen) { | |
946 | error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, | |
91447636 | 947 | UIO_SYSSPACE32, IO_NODELOCKED, cred, &count, (struct proc *)0); |
1c79356b A |
948 | /* |
949 | * Since we read MINDIRSIZ, residual must | |
950 | * be 0 unless we're at end of file. | |
951 | */ | |
952 | if (error || count != 0) | |
953 | return (0); | |
954 | #if 0 /*REV_ENDIAN_FS */ | |
955 | if (rev_endian) | |
956 | byte_swap_minidir_in(dp); | |
957 | #endif /* REV_ENDIAN_FS */ | |
958 | /* avoid infinite loops */ | |
959 | if (dp->d_reclen == 0) | |
960 | return (0); | |
961 | /* skip empty entries */ | |
962 | if (dp->d_ino == 0 || dp->d_ino == WINO) | |
963 | continue; | |
964 | /* accept only "." and ".." */ | |
965 | # if (BYTE_ORDER == LITTLE_ENDIAN) | |
966 | if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) | |
967 | namlen = dp->d_namlen; | |
968 | else | |
969 | namlen = dp->d_type; | |
970 | # else | |
971 | namlen = dp->d_namlen; | |
972 | # endif | |
973 | if (namlen > 2) | |
974 | return (0); | |
975 | if (dp->d_name[0] != '.') | |
976 | return (0); | |
977 | /* | |
978 | * At this point namlen must be 1 or 2. | |
979 | * 1 implies ".", 2 implies ".." if second | |
980 | * char is also "." | |
981 | */ | |
982 | if (namlen == 1) | |
983 | continue; | |
984 | if (dp->d_name[1] == '.' && dp->d_ino == parentino) | |
985 | continue; | |
986 | return (0); | |
987 | } | |
988 | return (1); | |
989 | } | |
990 | ||
991 | /* | |
992 | * Check if source directory is in the path of the target directory. | |
993 | * Target is supplied locked, source is unlocked. | |
1c79356b A |
994 | */ |
995 | int | |
996 | ufs_checkpath(source, target, cred) | |
997 | struct inode *source, *target; | |
91447636 | 998 | kauth_cred_t cred; |
1c79356b A |
999 | { |
1000 | struct vnode *vp; | |
1001 | int error, rootino, namlen; | |
91447636 | 1002 | int need_put = 0; |
1c79356b A |
1003 | struct dirtemplate dirbuf; |
1004 | ||
1005 | vp = ITOV(target); | |
1006 | if (target->i_number == source->i_number) { | |
1007 | error = EEXIST; | |
1008 | goto out; | |
1009 | } | |
1010 | rootino = ROOTINO; | |
1011 | error = 0; | |
1012 | if (target->i_number == rootino) | |
1013 | goto out; | |
1014 | ||
1015 | for (;;) { | |
1016 | if (vp->v_type != VDIR) { | |
1017 | error = ENOTDIR; | |
1018 | break; | |
1019 | } | |
1020 | error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, | |
91447636 | 1021 | sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE32, |
1c79356b A |
1022 | IO_NODELOCKED, cred, (int *)0, (struct proc *)0); |
1023 | if (error != 0) | |
1024 | break; | |
1025 | # if (BYTE_ORDER == LITTLE_ENDIAN) | |
1026 | if (vp->v_mount->mnt_maxsymlinklen > 0) | |
1027 | namlen = dirbuf.dotdot_namlen; | |
1028 | else | |
1029 | namlen = dirbuf.dotdot_type; | |
1030 | # else | |
1031 | namlen = dirbuf.dotdot_namlen; | |
1032 | # endif | |
1033 | if (namlen != 2 || | |
1034 | dirbuf.dotdot_name[0] != '.' || | |
1035 | dirbuf.dotdot_name[1] != '.') { | |
1036 | error = ENOTDIR; | |
1037 | break; | |
1038 | } | |
1039 | if (dirbuf.dotdot_ino == source->i_number) { | |
1040 | error = EINVAL; | |
1041 | break; | |
1042 | } | |
1043 | if (dirbuf.dotdot_ino == rootino) | |
1044 | break; | |
91447636 A |
1045 | |
1046 | if (need_put) | |
1047 | vnode_put(vp); | |
1048 | ||
1049 | if (error = VFS_VGET(vp->v_mount, (ino64_t)dirbuf.dotdot_ino, &vp, NULL)) { /* XXX need context */ | |
1c79356b A |
1050 | vp = NULL; |
1051 | break; | |
1052 | } | |
91447636 | 1053 | need_put = 1; |
1c79356b A |
1054 | } |
1055 | ||
1056 | out: | |
1057 | if (error == ENOTDIR) | |
1058 | printf("checkpath: .. not a directory\n"); | |
91447636 A |
1059 | if (need_put && vp) |
1060 | vnode_put(vp); | |
1061 | ||
1c79356b A |
1062 | return (error); |
1063 | } |