1 /* $NetBSD: ext2fs_rename.c,v 1.5 2013/01/22 09:39:15 dholland Exp $ */
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R Campbell.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ext2fs_rename.c,v 1.5 2013/01/22 09:39:15 dholland Exp $");
39 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/kauth.h>
43 #include <sys/mount.h>
44 #include <sys/namei.h>
45 #include <sys/vnode.h>
46 #include <sys/vnode_if.h>
48 #include <miscfs/genfs/genfs.h>
50 #include <ufs/ext2fs/ext2fs.h>
51 #include <ufs/ext2fs/ext2fs_dir.h>
52 #include <ufs/ext2fs/ext2fs_extern.h>
53 #include <ufs/ufs/inode.h>
54 #include <ufs/ufs/ufs_extern.h>
55 #include <ufs/ufs/ufsmount.h>
58 * Forward declarations
60 static int ext2fs_sane_rename(struct vnode
*, struct componentname
*,
61 struct vnode
*, struct componentname
*,
63 static bool ext2fs_rename_ulr_overlap_p(const struct ufs_lookup_results
*,
64 const struct ufs_lookup_results
*);
65 static int ext2fs_rename_recalculate_fulr(struct vnode
*,
66 struct ufs_lookup_results
*, const struct ufs_lookup_results
*,
67 const struct componentname
*);
68 static bool ext2fs_rmdired_p(struct vnode
*);
69 static int ext2fs_read_dotdot(struct vnode
*, kauth_cred_t
, ino_t
*);
70 static int ext2fs_rename_replace_dotdot(struct vnode
*,
71 struct vnode
*, struct vnode
*, kauth_cred_t
);
72 static int ext2fs_gro_lock_directory(struct mount
*, struct vnode
*);
74 static const struct genfs_rename_ops ext2fs_genfs_rename_ops
;
77 * ext2fs_sane_rename: The hairiest vop, with the saner API.
81 * . fdvp (from directory vnode),
82 * . fcnp (from component name),
83 * . tdvp (to directory vnode),
84 * . tcnp (to component name),
85 * . cred (credentials structure), and
86 * . posixly_correct (flag for behaviour if target & source link same file).
88 * fdvp and tdvp may be the same, and must be referenced and unlocked.
92 struct vnode
*fdvp
, struct componentname
*fcnp
,
93 struct vnode
*tdvp
, struct componentname
*tcnp
,
94 kauth_cred_t cred
, bool posixly_correct
)
96 struct ufs_lookup_results fulr
, tulr
;
98 return genfs_sane_rename(&ext2fs_genfs_rename_ops
,
99 fdvp
, fcnp
, &fulr
, tdvp
, tcnp
, &tulr
,
100 cred
, posixly_correct
);
104 * ext2fs_rename: The hairiest vop, with the insanest API. Defer to
105 * genfs_insane_rename immediately.
108 ext2fs_rename(void *v
)
111 return genfs_insane_rename(v
, &ext2fs_sane_rename
);
115 * ext2fs_gro_directory_empty_p: Return true if the directory vp is
116 * empty. dvp is its parent.
118 * vp and dvp must be locked and referenced.
121 ext2fs_gro_directory_empty_p(struct mount
*mp
, kauth_cred_t cred
,
122 struct vnode
*vp
, struct vnode
*dvp
)
128 KASSERT(dvp
!= NULL
);
130 KASSERT(vp
->v_mount
== mp
);
131 KASSERT(dvp
->v_mount
== mp
);
132 KASSERT(VOP_ISLOCKED(vp
) == LK_EXCLUSIVE
);
133 KASSERT(VOP_ISLOCKED(dvp
) == LK_EXCLUSIVE
);
135 return ext2fs_dirempty(VTOI(vp
), VTOI(dvp
)->i_number
, cred
);
139 * ext2fs_gro_rename_check_possible: Check whether a rename is possible
140 * independent of credentials.
143 ext2fs_gro_rename_check_possible(struct mount
*mp
,
144 struct vnode
*fdvp
, struct vnode
*fvp
,
145 struct vnode
*tdvp
, struct vnode
*tvp
)
150 KASSERT(fdvp
!= NULL
);
151 KASSERT(fvp
!= NULL
);
152 KASSERT(tdvp
!= NULL
);
153 KASSERT(fdvp
!= fvp
);
154 KASSERT(fdvp
!= tvp
);
155 KASSERT(tdvp
!= fvp
);
156 KASSERT(tdvp
!= tvp
);
158 KASSERT(fdvp
->v_type
== VDIR
);
159 KASSERT(tdvp
->v_type
== VDIR
);
160 KASSERT(fdvp
->v_mount
== mp
);
161 KASSERT(fvp
->v_mount
== mp
);
162 KASSERT(tdvp
->v_mount
== mp
);
163 KASSERT((tvp
== NULL
) || (tvp
->v_mount
== mp
));
164 KASSERT(VOP_ISLOCKED(fdvp
) == LK_EXCLUSIVE
);
165 KASSERT(VOP_ISLOCKED(fvp
) == LK_EXCLUSIVE
);
166 KASSERT(VOP_ISLOCKED(tdvp
) == LK_EXCLUSIVE
);
167 KASSERT((tvp
== NULL
) || (VOP_ISLOCKED(tvp
) == LK_EXCLUSIVE
));
169 return genfs_ufslike_rename_check_possible(
170 VTOI(fdvp
)->i_e2fs_flags
, VTOI(fvp
)->i_e2fs_flags
,
171 VTOI(tdvp
)->i_e2fs_flags
, (tvp
? VTOI(tvp
)->i_e2fs_flags
: 0),
173 EXT2_IMMUTABLE
, EXT2_APPEND
);
177 * ext2fs_gro_rename_check_permitted: Check whether a rename is
178 * permitted given our credentials.
181 ext2fs_gro_rename_check_permitted(struct mount
*mp
, kauth_cred_t cred
,
182 struct vnode
*fdvp
, struct vnode
*fvp
,
183 struct vnode
*tdvp
, struct vnode
*tvp
)
188 KASSERT(fdvp
!= NULL
);
189 KASSERT(fvp
!= NULL
);
190 KASSERT(tdvp
!= NULL
);
191 KASSERT(fdvp
!= fvp
);
192 KASSERT(fdvp
!= tvp
);
193 KASSERT(tdvp
!= fvp
);
194 KASSERT(tdvp
!= tvp
);
196 KASSERT(fdvp
->v_type
== VDIR
);
197 KASSERT(tdvp
->v_type
== VDIR
);
198 KASSERT(fdvp
->v_mount
== mp
);
199 KASSERT(fvp
->v_mount
== mp
);
200 KASSERT(tdvp
->v_mount
== mp
);
201 KASSERT((tvp
== NULL
) || (tvp
->v_mount
== mp
));
202 KASSERT(VOP_ISLOCKED(fdvp
) == LK_EXCLUSIVE
);
203 KASSERT(VOP_ISLOCKED(fvp
) == LK_EXCLUSIVE
);
204 KASSERT(VOP_ISLOCKED(tdvp
) == LK_EXCLUSIVE
);
205 KASSERT((tvp
== NULL
) || (VOP_ISLOCKED(tvp
) == LK_EXCLUSIVE
));
207 return genfs_ufslike_rename_check_permitted(cred
,
208 fdvp
, VTOI(fdvp
)->i_e2fs_mode
, VTOI(fdvp
)->i_uid
,
209 fvp
, VTOI(fvp
)->i_uid
,
210 tdvp
, VTOI(tdvp
)->i_e2fs_mode
, VTOI(tdvp
)->i_uid
,
211 tvp
, (tvp
? VTOI(tvp
)->i_uid
: 0));
215 * ext2fs_gro_remove_check_possible: Check whether a remove is possible
216 * independent of credentials.
219 ext2fs_gro_remove_check_possible(struct mount
*mp
,
220 struct vnode
*dvp
, struct vnode
*vp
)
225 KASSERT(dvp
!= NULL
);
228 KASSERT(dvp
->v_type
== VDIR
);
229 KASSERT(vp
->v_type
!= VDIR
);
230 KASSERT(dvp
->v_mount
== mp
);
231 KASSERT(vp
->v_mount
== mp
);
232 KASSERT(VOP_ISLOCKED(dvp
) == LK_EXCLUSIVE
);
233 KASSERT(VOP_ISLOCKED(vp
) == LK_EXCLUSIVE
);
235 return genfs_ufslike_remove_check_possible(
236 VTOI(dvp
)->i_e2fs_flags
, VTOI(vp
)->i_e2fs_flags
,
237 EXT2_IMMUTABLE
, EXT2_APPEND
);
241 * ext2fs_gro_remove_check_permitted: Check whether a remove is
242 * permitted given our credentials.
245 ext2fs_gro_remove_check_permitted(struct mount
*mp
, kauth_cred_t cred
,
246 struct vnode
*dvp
, struct vnode
*vp
)
251 KASSERT(dvp
!= NULL
);
254 KASSERT(dvp
->v_type
== VDIR
);
255 KASSERT(vp
->v_type
!= VDIR
);
256 KASSERT(dvp
->v_mount
== mp
);
257 KASSERT(vp
->v_mount
== mp
);
258 KASSERT(VOP_ISLOCKED(dvp
) == LK_EXCLUSIVE
);
259 KASSERT(VOP_ISLOCKED(vp
) == LK_EXCLUSIVE
);
261 return genfs_ufslike_remove_check_permitted(cred
,
262 dvp
, VTOI(dvp
)->i_e2fs_mode
, VTOI(dvp
)->i_uid
,
263 vp
, VTOI(vp
)->i_uid
);
267 * ext2fs_gro_rename: Actually perform the rename operation.
270 ext2fs_gro_rename(struct mount
*mp
, kauth_cred_t cred
,
271 struct vnode
*fdvp
, struct componentname
*fcnp
,
272 void *fde
, struct vnode
*fvp
,
273 struct vnode
*tdvp
, struct componentname
*tcnp
,
274 void *tde
, struct vnode
*tvp
)
276 struct ufs_lookup_results
*fulr
= fde
;
277 struct ufs_lookup_results
*tulr
= tde
;
278 bool directory_p
, reparent_p
;
283 KASSERT(fdvp
!= NULL
);
284 KASSERT(fcnp
!= NULL
);
285 KASSERT(fulr
!= NULL
);
286 KASSERT(fvp
!= NULL
);
287 KASSERT(tdvp
!= NULL
);
288 KASSERT(tcnp
!= NULL
);
289 KASSERT(tulr
!= NULL
);
290 KASSERT(fulr
!= tulr
);
291 KASSERT(fdvp
!= fvp
);
292 KASSERT(fdvp
!= tvp
);
293 KASSERT(tdvp
!= fvp
);
294 KASSERT(tdvp
!= tvp
);
296 KASSERT(fdvp
->v_mount
== mp
);
297 KASSERT(fvp
->v_mount
== mp
);
298 KASSERT(tdvp
->v_mount
== mp
);
299 KASSERT((tvp
== NULL
) || (tvp
->v_mount
== mp
));
300 KASSERT(VOP_ISLOCKED(fdvp
) == LK_EXCLUSIVE
);
301 KASSERT(VOP_ISLOCKED(fvp
) == LK_EXCLUSIVE
);
302 KASSERT(VOP_ISLOCKED(tdvp
) == LK_EXCLUSIVE
);
303 KASSERT((tvp
== NULL
) || (VOP_ISLOCKED(tvp
) == LK_EXCLUSIVE
));
306 * We shall need to temporarily bump the link count, so make
307 * sure there is room to do so.
309 if ((nlink_t
)VTOI(fvp
)->i_e2fs_nlink
>= LINK_MAX
)
312 directory_p
= (fvp
->v_type
== VDIR
);
313 KASSERT(directory_p
== ((VTOI(fvp
)->i_e2fs_mode
& IFMT
) == IFDIR
));
314 KASSERT((tvp
== NULL
) || (directory_p
== (tvp
->v_type
== VDIR
)));
315 KASSERT((tvp
== NULL
) || (directory_p
==
316 ((VTOI(tvp
)->i_e2fs_mode
& IFMT
) == IFDIR
)));
318 reparent_p
= (fdvp
!= tdvp
);
319 KASSERT(reparent_p
== (VTOI(fdvp
)->i_number
!= VTOI(tdvp
)->i_number
));
322 * Commence hacking of the data on disk.
326 * 1) Bump link count while we're moving stuff
327 * around. If we crash somewhere before
328 * completing our work, the link count
329 * may be wrong, but correctable.
332 KASSERT((nlink_t
)VTOI(fvp
)->i_e2fs_nlink
< LINK_MAX
);
333 VTOI(fvp
)->i_e2fs_nlink
++;
334 VTOI(fvp
)->i_flag
|= IN_CHANGE
;
335 error
= ext2fs_update(fvp
, NULL
, NULL
, UPDATE_WAIT
);
337 goto whymustithurtsomuch
;
340 * 2) If target doesn't exist, link the target
341 * to the source and unlink the source.
342 * Otherwise, rewrite the target directory
343 * entry to reference the source inode and
344 * expunge the original entry's existence.
349 * Account for ".." in new directory.
350 * When source and destination have the same
351 * parent we don't fool with the link count.
353 if (directory_p
&& reparent_p
) {
354 if ((nlink_t
)VTOI(tdvp
)->i_e2fs_nlink
>= LINK_MAX
) {
356 goto whymustithurtsomuch
;
358 KASSERT((nlink_t
)VTOI(tdvp
)->i_e2fs_nlink
< LINK_MAX
);
359 VTOI(tdvp
)->i_e2fs_nlink
++;
360 VTOI(tdvp
)->i_flag
|= IN_CHANGE
;
361 error
= ext2fs_update(tdvp
, NULL
, NULL
, UPDATE_WAIT
);
364 * Link count update didn't take --
365 * back out the in-memory link count.
367 KASSERT(0 < VTOI(tdvp
)->i_e2fs_nlink
);
368 VTOI(tdvp
)->i_e2fs_nlink
--;
369 VTOI(tdvp
)->i_flag
|= IN_CHANGE
;
370 goto whymustithurtsomuch
;
374 error
= ext2fs_direnter(VTOI(fvp
), tdvp
, tulr
, tcnp
);
376 if (directory_p
&& reparent_p
) {
378 * Directory update didn't take, but
379 * the link count update did -- back
380 * out the in-memory link count and the
381 * on-disk link count.
383 KASSERT(0 < VTOI(tdvp
)->i_e2fs_nlink
);
384 VTOI(tdvp
)->i_e2fs_nlink
--;
385 VTOI(tdvp
)->i_flag
|= IN_CHANGE
;
386 (void)ext2fs_update(tdvp
, NULL
, NULL
,
389 goto whymustithurtsomuch
;
393 /* XXX WTF? Why purge here? Why not purge others? */
397 * Make the target directory's entry for tcnp point at
400 error
= ext2fs_dirrewrite(VTOI(tdvp
), tulr
, VTOI(fvp
), tcnp
);
402 goto whymustithurtsomuch
;
405 * If the source and target are directories, and the
406 * target is in the same directory as the source,
407 * decrement the link count of the common parent
408 * directory, since we are removing the target from
411 if (directory_p
&& !reparent_p
) {
412 KASSERT(fdvp
== tdvp
);
413 /* XXX check, don't kassert */
414 KASSERT(0 < VTOI(tdvp
)->i_e2fs_nlink
);
415 VTOI(tdvp
)->i_e2fs_nlink
--;
416 VTOI(tdvp
)->i_flag
|= IN_CHANGE
;
420 * Adjust the link count of the target to
421 * reflect the dirrewrite above. If this is
422 * a directory it is empty and there are
423 * no links to it, so we can squash the inode and
424 * any space associated with it. We disallowed
425 * renaming over top of a directory with links to
426 * it above, as the remaining link would point to
427 * a directory without "." or ".." entries.
429 /* XXX check, don't kassert */
430 KASSERT(0 < VTOI(tvp
)->i_e2fs_nlink
);
431 VTOI(tvp
)->i_e2fs_nlink
--;
434 * XXX The ext2fs_dirempty call earlier does
435 * not guarantee anything about nlink.
437 if (VTOI(tvp
)->i_e2fs_nlink
!= 1)
438 ufs_dirbad(VTOI(tvp
), (doff_t
)0,
439 "hard-linked directory");
440 VTOI(tvp
)->i_e2fs_nlink
= 0;
441 error
= ext2fs_truncate(tvp
, (off_t
)0, IO_SYNC
, cred
);
442 #if 0 /* XXX This branch was not in ext2fs_rename! */
444 goto whymustithurtsomuch
;
448 * XXX Why is this here, and not above the preceding
451 VTOI(tvp
)->i_flag
|= IN_CHANGE
;
455 * If the source is a directory with a new parent, the link
456 * count of the old parent directory must be decremented and
457 * ".." set to point to the new parent.
459 if (directory_p
&& reparent_p
) {
460 error
= ext2fs_rename_replace_dotdot(fvp
, fdvp
, tdvp
, cred
);
462 goto whymustithurtsomuch
;
464 /* XXX WTF? Why purge here? Why not purge others? */
469 * 3) Unlink the source.
473 * ext2fs_direnter may compact the directory in the process of
474 * inserting a new entry. That may invalidate fulr, which we
475 * need in order to remove the old entry. In that case, we
476 * need to recalculate what fulr should be.
478 if (!reparent_p
&& (tvp
== NULL
) &&
479 ext2fs_rename_ulr_overlap_p(fulr
, tulr
)) {
480 error
= ext2fs_rename_recalculate_fulr(fdvp
, fulr
, tulr
, fcnp
);
482 if (error
) /* XXX Try to back out changes? */
483 goto whymustithurtsomuch
;
487 error
= ext2fs_dirremove(fdvp
, fulr
, fcnp
);
489 goto whymustithurtsomuch
;
492 * XXX Perhaps this should go at the top, in case the file
493 * system is modified but incompletely so because of an
494 * intermediate error.
496 genfs_rename_knote(fdvp
, fvp
, tdvp
, tvp
,
497 ((tvp
!= NULL
) && (VTOI(tvp
)->i_e2fs_nlink
== 0)));
499 genfs_rename_cache_purge(fdvp
, fvp
, tdvp
, tvp
);
503 KASSERT(0 < VTOI(fvp
)->i_e2fs_nlink
);
504 VTOI(fvp
)->i_e2fs_nlink
--;
505 VTOI(fvp
)->i_flag
|= IN_CHANGE
;
510 * ext2fs_rename_ulr_overlap_p: True iff tulr overlaps with fulr so
511 * that entering a directory entry at tulr may move fulr.
514 ext2fs_rename_ulr_overlap_p(const struct ufs_lookup_results
*fulr
,
515 const struct ufs_lookup_results
*tulr
)
517 doff_t from_prev_start
, from_prev_end
, to_start
, to_end
;
519 KASSERT(fulr
!= NULL
);
520 KASSERT(tulr
!= NULL
);
521 KASSERT(fulr
!= tulr
);
524 * fulr is from a DELETE lookup, so fulr->ulr_count is the size
525 * of the preceding entry (d_reclen).
527 from_prev_end
= fulr
->ulr_offset
;
528 KASSERT(fulr
->ulr_count
<= from_prev_end
);
529 from_prev_start
= (from_prev_end
- fulr
->ulr_count
);
532 * tulr is from a RENAME lookup, so tulr->ulr_count is the size
533 * of the free space for an entry that we are about to fill.
535 to_start
= tulr
->ulr_offset
;
536 KASSERT(tulr
->ulr_count
< (EXT2FS_MAXDIRSIZE
- to_start
));
537 to_end
= (to_start
+ tulr
->ulr_count
);
540 (((to_start
<= from_prev_start
) && (from_prev_start
< to_end
)) ||
541 ((to_start
<= from_prev_end
) && (from_prev_end
< to_end
)));
545 * ext2fs_rename_recalculate_fulr: If we have just entered a directory
546 * into dvp at tulr, and we were about to remove one at fulr for an
547 * entry named fcnp, fulr may be invalid. So, if necessary,
551 ext2fs_rename_recalculate_fulr(struct vnode
*dvp
,
552 struct ufs_lookup_results
*fulr
, const struct ufs_lookup_results
*tulr
,
553 const struct componentname
*fcnp
)
556 struct ufsmount
*ump
;
557 /* XXX int is a silly type for this; blame ufsmount::um_dirblksiz. */
559 doff_t search_start
, search_end
;
560 doff_t offset
; /* Offset of entry we're examining. */
561 struct buf
*bp
; /* I/O block we're examining. */
562 char *dirbuf
; /* Pointer into directory at search_start. */
563 struct ext2fs_direct
*ep
; /* Pointer to the entry we're examining. */
564 /* XXX direct::d_reclen is 16-bit;
565 * ufs_lookup_results::ulr_reclen is 32-bit. Blah. */
566 uint32_t reclen
; /* Length of the entry we're examining. */
567 uint32_t prev_reclen
; /* Length of the preceding entry. */
570 KASSERT(dvp
!= NULL
);
571 KASSERT(dvp
->v_mount
!= NULL
);
572 KASSERT(VTOI(dvp
) != NULL
);
573 KASSERT(fulr
!= NULL
);
574 KASSERT(tulr
!= NULL
);
575 KASSERT(fulr
!= tulr
);
576 KASSERT(ext2fs_rename_ulr_overlap_p(fulr
, tulr
));
580 KASSERT(ump
!= NULL
);
581 KASSERT(ump
== VTOI(dvp
)->i_ump
);
583 dirblksiz
= ump
->um_dirblksiz
;
584 KASSERT(0 < dirblksiz
);
585 KASSERT((dirblksiz
& (dirblksiz
- 1)) == 0);
587 /* A directory block may not span across multiple I/O blocks. */
588 KASSERT(dirblksiz
<= mp
->mnt_stat
.f_iosize
);
590 /* Find the bounds of the search. */
591 search_start
= tulr
->ulr_offset
;
592 KASSERT(fulr
->ulr_reclen
< (EXT2FS_MAXDIRSIZE
- fulr
->ulr_offset
));
593 search_end
= (fulr
->ulr_offset
+ fulr
->ulr_reclen
);
595 /* Compaction must happen only within a directory block. (*) */
596 KASSERT(search_start
<= search_end
);
597 KASSERT((search_end
- (search_start
&~ (dirblksiz
- 1))) <= dirblksiz
);
601 error
= ext2fs_blkatoff(dvp
, (off_t
)search_start
, &dirbuf
, &bp
);
604 KASSERT(dirbuf
!= NULL
);
608 * Guarantee we sha'n't go past the end of the buffer we got.
609 * dirbuf is bp->b_data + (search_start & (iosize - 1)), and
610 * the valid range is [bp->b_data, bp->b_data + bp->b_bcount).
612 KASSERT((search_end
- search_start
) <=
613 (bp
->b_bcount
- (search_start
& (mp
->mnt_stat
.f_iosize
- 1))));
615 prev_reclen
= fulr
->ulr_count
;
616 offset
= search_start
;
619 * Search from search_start to search_end for the entry matching
620 * fcnp, which must be there because we found it before and it
621 * should only at most have moved earlier.
624 KASSERT(search_start
<= offset
);
625 KASSERT(offset
< search_end
);
628 * Examine the directory entry at offset.
630 ep
= (struct ext2fs_direct
*)
631 (dirbuf
+ (offset
- search_start
));
632 reclen
= fs2h16(ep
->e2d_reclen
);
634 if (ep
->e2d_ino
== 0)
635 goto next
; /* Entry is unused. */
637 if (fs2h32(ep
->e2d_ino
) == UFS_WINO
)
638 goto next
; /* Entry is whiteout. */
640 if (fcnp
->cn_namelen
!= ep
->e2d_namlen
)
641 goto next
; /* Wrong name length. */
643 if (memcmp(ep
->e2d_name
, fcnp
->cn_nameptr
, fcnp
->cn_namelen
))
644 goto next
; /* Wrong name. */
650 if (! ((reclen
< search_end
) &&
651 (offset
< (search_end
- reclen
)))) {
653 return EIO
; /* XXX Panic? What? */
656 /* We may not move past the search end. */
657 KASSERT(reclen
< search_end
);
658 KASSERT(offset
< (search_end
- reclen
));
661 * We may not move across a directory block boundary;
664 KASSERT((offset
&~ (dirblksiz
- 1)) ==
665 ((offset
+ reclen
) &~ (dirblksiz
- 1)));
667 prev_reclen
= reclen
;
672 * Found the entry. Record where.
674 fulr
->ulr_offset
= offset
;
675 fulr
->ulr_reclen
= reclen
;
678 * Record the preceding record length, but not if we're at the
679 * start of a directory block.
681 fulr
->ulr_count
= ((offset
& (dirblksiz
- 1))? prev_reclen
: 0);
688 * ext2fs_gro_remove: Rename an object over another link to itself,
689 * effectively removing just the original link.
692 ext2fs_gro_remove(struct mount
*mp
, kauth_cred_t cred
,
693 struct vnode
*dvp
, struct componentname
*cnp
, void *de
, struct vnode
*vp
)
695 struct ufs_lookup_results
*ulr
= de
;
700 KASSERT(dvp
!= NULL
);
701 KASSERT(cnp
!= NULL
);
702 KASSERT(ulr
!= NULL
);
705 KASSERT(dvp
->v_mount
== mp
);
706 KASSERT(vp
->v_mount
== mp
);
707 KASSERT(dvp
->v_type
== VDIR
);
708 KASSERT(vp
->v_type
!= VDIR
);
709 KASSERT(VOP_ISLOCKED(dvp
) == LK_EXCLUSIVE
);
710 KASSERT(VOP_ISLOCKED(vp
) == LK_EXCLUSIVE
);
712 error
= ext2fs_dirremove(dvp
, ulr
, cnp
);
716 KASSERT(0 < VTOI(vp
)->i_e2fs_nlink
);
717 VTOI(vp
)->i_e2fs_nlink
--;
718 VTOI(vp
)->i_flag
|= IN_CHANGE
;
720 VN_KNOTE(dvp
, NOTE_WRITE
);
721 VN_KNOTE(vp
, (VTOI(vp
)->i_e2fs_nlink
? NOTE_LINK
: NOTE_DELETE
));
727 * ext2fs_gro_lookup: Look up and save the lookup results.
730 ext2fs_gro_lookup(struct mount
*mp
, struct vnode
*dvp
,
731 struct componentname
*cnp
, void *de_ret
, struct vnode
**vp_ret
)
733 struct ufs_lookup_results
*ulr_ret
= de_ret
;
739 KASSERT(dvp
!= NULL
);
740 KASSERT(cnp
!= NULL
);
741 KASSERT(ulr_ret
!= NULL
);
742 KASSERT(vp_ret
!= NULL
);
743 KASSERT(VOP_ISLOCKED(dvp
) == LK_EXCLUSIVE
);
745 /* Kludge cargo-culted from dholland's ufs_rename. */
746 cnp
->cn_flags
&=~ MODMASK
;
747 cnp
->cn_flags
|= (LOCKPARENT
| LOCKLEAF
);
749 error
= relookup(dvp
, &vp
, cnp
, 0 /* dummy */);
750 if ((error
== 0) && (vp
== NULL
)) {
758 * Thanks to VFS insanity, relookup locks vp, which screws us
764 out
: *ulr_ret
= VTOI(dvp
)->i_crap
;
770 * ext2fs_rmdired_p: Check whether the directory vp has been rmdired.
772 * vp must be locked and referenced.
775 ext2fs_rmdired_p(struct vnode
*vp
)
779 KASSERT(VOP_ISLOCKED(vp
) == LK_EXCLUSIVE
);
780 KASSERT(vp
->v_type
== VDIR
);
782 /* XXX Is this correct? */
783 return (ext2fs_size(VTOI(vp
)) == 0);
787 * ext2fs_gro_genealogy: Analyze the genealogy of the source and target
791 ext2fs_gro_genealogy(struct mount
*mp
, kauth_cred_t cred
,
792 struct vnode
*fdvp
, struct vnode
*tdvp
,
793 struct vnode
**intermediate_node_ret
)
795 struct vnode
*vp
, *dvp
;
800 KASSERT(fdvp
!= NULL
);
801 KASSERT(tdvp
!= NULL
);
802 KASSERT(fdvp
!= tdvp
);
803 KASSERT(intermediate_node_ret
!= NULL
);
804 KASSERT(fdvp
->v_mount
== mp
);
805 KASSERT(tdvp
->v_mount
== mp
);
806 KASSERT(fdvp
->v_type
== VDIR
);
807 KASSERT(tdvp
->v_type
== VDIR
);
810 * We need to provisionally lock tdvp to keep rmdir from
811 * deleting it -- or any ancestor -- at an inopportune moment.
813 error
= ext2fs_gro_lock_directory(mp
, tdvp
);
822 KASSERT(VOP_ISLOCKED(vp
) == LK_EXCLUSIVE
);
823 KASSERT(vp
->v_mount
== mp
);
824 KASSERT(vp
->v_type
== VDIR
);
825 KASSERT(!ext2fs_rmdired_p(vp
));
827 /* Did we hit the root without finding fdvp? */
828 if (VTOI(vp
)->i_number
== UFS_ROOTINO
) {
830 *intermediate_node_ret
= NULL
;
834 error
= ext2fs_read_dotdot(vp
, cred
, &dotdot_ino
);
840 /* Did we find that fdvp is an ancestor of tdvp? */
841 if (VTOI(fdvp
)->i_number
== dotdot_ino
) {
842 /* Unlock vp, but keep it referenced. */
844 *intermediate_node_ret
= vp
;
848 /* Neither -- keep ascending the family tree. */
851 * Unlock vp so that we can lock the parent, but keep
852 * vp referenced until after we have found the parent,
853 * so that dotdot_ino will not be recycled.
855 * XXX This guarantees that vp's inode number will not
856 * be recycled, but why can't dotdot_ino be recycled?
859 error
= VFS_VGET(mp
, dotdot_ino
, &dvp
);
864 KASSERT(dvp
!= NULL
);
865 KASSERT(VOP_ISLOCKED(dvp
) == LK_EXCLUSIVE
);
868 if (vp
->v_type
!= VDIR
) {
870 * XXX Panic? Print a warning? Can this
871 * happen if we lose the race I suspect to
872 * exist above, and the `..' inode number has
879 if (ext2fs_rmdired_p(vp
)) {
887 * ext2fs_read_dotdot: Store in *ino_ret the inode number of the parent
888 * of the directory vp.
891 ext2fs_read_dotdot(struct vnode
*vp
, kauth_cred_t cred
, ino_t
*ino_ret
)
893 struct ext2fs_dirtemplate dirbuf
;
897 KASSERT(ino_ret
!= NULL
);
898 KASSERT(vp
->v_type
== VDIR
);
900 error
= vn_rdwr(UIO_READ
, vp
, &dirbuf
, sizeof dirbuf
, (off_t
)0,
901 UIO_SYSSPACE
, IO_NODELOCKED
, cred
, NULL
, NULL
);
905 if (dirbuf
.dotdot_namlen
!= 2 ||
906 dirbuf
.dotdot_name
[0] != '.' ||
907 dirbuf
.dotdot_name
[1] != '.')
908 /* XXX Panic? Print warning? */
911 *ino_ret
= fs2h32(dirbuf
.dotdot_ino
);
916 * ext2fs_rename_replace_dotdot: Change the target of the `..' entry of
917 * the directory vp from fdvp to tdvp.
920 ext2fs_rename_replace_dotdot(struct vnode
*vp
,
921 struct vnode
*fdvp
, struct vnode
*tdvp
,
924 struct ext2fs_dirtemplate dirbuf
;
927 /* XXX Does it make sense to do this before the sanity checks below? */
928 KASSERT(0 < VTOI(fdvp
)->i_e2fs_nlink
);
929 VTOI(fdvp
)->i_e2fs_nlink
--;
930 VTOI(fdvp
)->i_flag
|= IN_CHANGE
;
932 error
= vn_rdwr(UIO_READ
, vp
, &dirbuf
, sizeof dirbuf
, (off_t
)0,
933 UIO_SYSSPACE
, IO_NODELOCKED
, cred
, NULL
, NULL
);
937 if (dirbuf
.dotdot_namlen
!= 2 ||
938 dirbuf
.dotdot_name
[0] != '.' ||
939 dirbuf
.dotdot_name
[1] != '.') {
940 ufs_dirbad(VTOI(vp
), (doff_t
)12, "bad `..' entry");
944 if (fs2h32(dirbuf
.dotdot_ino
) != VTOI(fdvp
)->i_number
) {
945 ufs_dirbad(VTOI(vp
), (doff_t
)12,
946 "`..' does not point at parent");
950 dirbuf
.dotdot_ino
= h2fs32(VTOI(tdvp
)->i_number
);
951 /* XXX WTF? Why not check error? */
952 (void)vn_rdwr(UIO_WRITE
, vp
, &dirbuf
, sizeof dirbuf
, (off_t
)0,
953 UIO_SYSSPACE
, (IO_NODELOCKED
| IO_SYNC
), cred
, NULL
, NULL
);
959 * ext2fs_gro_lock_directory: Lock the directory vp, but fail if it has
963 ext2fs_gro_lock_directory(struct mount
*mp
, struct vnode
*vp
)
969 KASSERT(vp
->v_mount
== mp
);
971 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
973 if (ext2fs_rmdired_p(vp
)) {
981 static const struct genfs_rename_ops ext2fs_genfs_rename_ops
= {
982 .gro_directory_empty_p
= ext2fs_gro_directory_empty_p
,
983 .gro_rename_check_possible
= ext2fs_gro_rename_check_possible
,
984 .gro_rename_check_permitted
= ext2fs_gro_rename_check_permitted
,
985 .gro_remove_check_possible
= ext2fs_gro_remove_check_possible
,
986 .gro_remove_check_permitted
= ext2fs_gro_remove_check_permitted
,
987 .gro_rename
= ext2fs_gro_rename
,
988 .gro_remove
= ext2fs_gro_remove
,
989 .gro_lookup
= ext2fs_gro_lookup
,
990 .gro_genealogy
= ext2fs_gro_genealogy
,
991 .gro_lock_directory
= ext2fs_gro_lock_directory
,