Deleted Added
sdiff udiff text old ( 68307 ) new ( 76167 )
full compact
1/*
2 * modified for EXT2FS support in Lites 1.1
3 *
4 * Aug 1995, Godmar Back (gback@cs.utah.edu)
5 * University of Utah, Department of Computer Science
6 */
7/*
8 * Copyright (c) 1982, 1986, 1989, 1993
9 * The Regents of the University of California. All rights reserved.
10 * (c) UNIX System Laboratories, Inc.
11 * All or some portions of this file are derived from material licensed
12 * to the University of California by American Telephone and Telegraph
13 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
14 * the permission of UNIX System Laboratories, Inc.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
45 * @(#)ext2_vnops.c 8.7 (Berkeley) 2/3/94
46 * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_vnops.c 76167 2001-05-01 08:34:45Z phk $
47 */
48
49#include "opt_quota.h"
50#include "opt_suiddir.h"
51
52#include <sys/param.h>
53#include <sys/systm.h>
54#include <sys/resourcevar.h>
55#include <sys/kernel.h>
56#include <sys/stat.h>
57#include <sys/bio.h>
58#include <sys/buf.h>
59#include <sys/proc.h>
60#include <sys/mount.h>
61#include <sys/time.h>
62#include <sys/vnode.h>
63#include <sys/namei.h>
64
65#include <vm/vm.h>
66#include <vm/vm_extern.h>
67#include <vm/vnode_pager.h>
68
69#include <sys/signalvar.h>
70#include <ufs/ufs/dir.h>
71#include <ufs/ufs/extattr.h>
72#include <ufs/ufs/quota.h>
73#include <ufs/ufs/inode.h>
74#include <ufs/ufs/ufsmount.h>
75#include <ufs/ufs/ufs_extern.h>
76
77#include <gnu/ext2fs/ext2_fs_sb.h>
78#include <gnu/ext2fs/fs.h>
79#include <gnu/ext2fs/ext2_extern.h>
80#include <gnu/ext2fs/ext2_fs.h>
81
82static int ext2_makeinode __P((int mode, struct vnode *, struct vnode **, struct componentname *));
83
84static int ext2_fsync __P((struct vop_fsync_args *));
85static int ext2_read __P((struct vop_read_args *));
86static int ext2_write __P((struct vop_write_args *));
87static int ext2_remove __P((struct vop_remove_args *));
88static int ext2_link __P((struct vop_link_args *));
89static int ext2_rename __P((struct vop_rename_args *));
90static int ext2_mkdir __P((struct vop_mkdir_args *));
91static int ext2_rmdir __P((struct vop_rmdir_args *));
92static int ext2_create __P((struct vop_create_args *));
93static int ext2_mknod __P((struct vop_mknod_args *));
94static int ext2_symlink __P((struct vop_symlink_args *));
95
96/* Global vfs data structures for ufs. */
97vop_t **ext2_vnodeop_p;
98static struct vnodeopv_entry_desc ext2_vnodeop_entries[] = {
99 { &vop_default_desc, (vop_t *) ufs_vnoperate },
100 { &vop_cachedlookup_desc, (vop_t *) ext2_lookup },
101 { &vop_fsync_desc, (vop_t *) ext2_fsync },
102 { &vop_inactive_desc, (vop_t *) ext2_inactive },
103 { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
104 { &vop_read_desc, (vop_t *) ext2_read },
105 { &vop_readdir_desc, (vop_t *) ext2_readdir },
106 { &vop_reallocblks_desc, (vop_t *) ext2_reallocblks },
107 { &vop_write_desc, (vop_t *) ext2_write },
108 { &vop_remove_desc, (vop_t *) ext2_remove },
109 { &vop_link_desc, (vop_t *) ext2_link },
110 { &vop_rename_desc, (vop_t *) ext2_rename },
111 { &vop_mkdir_desc, (vop_t *) ext2_mkdir },
112 { &vop_rmdir_desc, (vop_t *) ext2_rmdir },
113 { &vop_create_desc, (vop_t *) ext2_create },
114 { &vop_mknod_desc, (vop_t *) ext2_mknod },
115 { &vop_symlink_desc, (vop_t *) ext2_symlink },
116 { NULL, NULL }
117};
118static struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
119 { &ext2_vnodeop_p, ext2_vnodeop_entries };
120
121vop_t **ext2_specop_p;
122static struct vnodeopv_entry_desc ext2_specop_entries[] = {
123 { &vop_default_desc, (vop_t *) ufs_vnoperatespec },
124 { &vop_fsync_desc, (vop_t *) ext2_fsync },
125 { &vop_inactive_desc, (vop_t *) ext2_inactive },
126 { NULL, NULL }
127};
128static struct vnodeopv_desc ext2fs_specop_opv_desc =
129 { &ext2_specop_p, ext2_specop_entries };
130
131vop_t **ext2_fifoop_p;
132static struct vnodeopv_entry_desc ext2_fifoop_entries[] = {
133 { &vop_default_desc, (vop_t *) ufs_vnoperatefifo },
134 { &vop_fsync_desc, (vop_t *) ext2_fsync },
135 { &vop_inactive_desc, (vop_t *) ext2_inactive },
136 { NULL, NULL }
137};
138static struct vnodeopv_desc ext2fs_fifoop_opv_desc =
139 { &ext2_fifoop_p, ext2_fifoop_entries };
140
141 VNODEOP_SET(ext2fs_vnodeop_opv_desc);
142 VNODEOP_SET(ext2fs_specop_opv_desc);
143 VNODEOP_SET(ext2fs_fifoop_opv_desc);
144
145#include <gnu/ext2fs/ext2_readwrite.c>
146
147/*
148 * A virgin directory (no blushing please).
149 * Note that the type and namlen fields are reversed relative to ufs.
150 * Also, we don't use `struct odirtemplate', since it would just cause
151 * endianness problems.
152 */
153static struct dirtemplate mastertemplate = {
154 0, 12, 1, EXT2_FT_DIR, ".",
155 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
156};
157static struct dirtemplate omastertemplate = {
158 0, 12, 1, EXT2_FT_UNKNOWN, ".",
159 0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".."
160};
161
162/*
163 * Create a regular file
164 */
165static int
166ext2_create(ap)
167 struct vop_create_args /* {
168 struct vnode *a_dvp;
169 struct vnode **a_vpp;
170 struct componentname *a_cnp;
171 struct vattr *a_vap;
172 } */ *ap;
173{
174 int error;
175
176 error =
177 ext2_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
178 ap->a_dvp, ap->a_vpp, ap->a_cnp);
179 if (error)
180 return (error);
181 return (0);
182}
183
184/*
185 * Synch an open file.
186 */
187/* ARGSUSED */
188static int
189ext2_fsync(ap)
190 struct vop_fsync_args /* {
191 struct vnode *a_vp;
192 struct ucred *a_cred;
193 int a_waitfor;
194 struct proc *a_p;
195 } */ *ap;
196{
197 register struct vnode *vp = ap->a_vp;
198 register struct buf *bp;
199 struct buf *nbp;
200 int s;
201
202 /*
203 * XXX why is all this fs specific?
204 */
205
206 /*
207 * Flush all dirty buffers associated with a vnode.
208 */
209 ext2_discard_prealloc(VTOI(vp));
210
211loop:
212 s = splbio();
213 for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
214 nbp = TAILQ_NEXT(bp, b_vnbufs);
215 if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT))
216 continue;
217 if ((bp->b_flags & B_DELWRI) == 0)
218 panic("ext2_fsync: not dirty");
219 bremfree(bp);
220 splx(s);
221 /*
222 * Wait for I/O associated with indirect blocks to complete,
223 * since there is no way to quickly wait for them below.
224 */
225 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
226 (void) bawrite(bp);
227 else
228 (void) bwrite(bp);
229 goto loop;
230 }
231 if (ap->a_waitfor == MNT_WAIT) {
232 while (vp->v_numoutput) {
233 vp->v_flag |= VBWAIT;
234 tsleep(&vp->v_numoutput, PRIBIO + 1, "e2fsyn", 0);
235 }
236#if DIAGNOSTIC
237 if (!TAILQ_EMPTY(&vp->v_dirtyblkhd)) {
238 vprint("ext2_fsync: dirty", vp);
239 goto loop;
240 }
241#endif
242 }
243 splx(s);
244 return (UFS_UPDATE(ap->a_vp, ap->a_waitfor == MNT_WAIT));
245}
246
247/*
248 * Mknod vnode call
249 */
250/* ARGSUSED */
251static int
252ext2_mknod(ap)
253 struct vop_mknod_args /* {
254 struct vnode *a_dvp;
255 struct vnode **a_vpp;
256 struct componentname *a_cnp;
257 struct vattr *a_vap;
258 } */ *ap;
259{
260 struct vattr *vap = ap->a_vap;
261 struct vnode **vpp = ap->a_vpp;
262 struct inode *ip;
263 ino_t ino;
264 int error;
265
266 error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
267 ap->a_dvp, vpp, ap->a_cnp);
268 if (error)
269 return (error);
270 ip = VTOI(*vpp);
271 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
272 if (vap->va_rdev != VNOVAL) {
273 /*
274 * Want to be able to use this to make badblock
275 * inodes, so don't truncate the dev number.
276 */
277 ip->i_rdev = vap->va_rdev;
278 }
279 /*
280 * Remove inode, then reload it through VFS_VGET so it is
281 * checked to see if it is an alias of an existing entry in
282 * the inode cache.
283 */
284 vput(*vpp);
285 (*vpp)->v_type = VNON;
286 ino = ip->i_number; /* Save this before vgone() invalidates ip. */
287 vgone(*vpp);
288 error = VFS_VGET(ap->a_dvp->v_mount, ino, vpp);
289 if (error) {
290 *vpp = NULL;
291 return (error);
292 }
293 return (0);
294}
295
296static int
297ext2_remove(ap)
298 struct vop_remove_args /* {
299 struct vnode *a_dvp;
300 struct vnode *a_vp;
301 struct componentname *a_cnp;
302 } */ *ap;
303{
304 struct inode *ip;
305 struct vnode *vp = ap->a_vp;
306 struct vnode *dvp = ap->a_dvp;
307 int error;
308
309 ip = VTOI(vp);
310 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
311 (VTOI(dvp)->i_flags & APPEND)) {
312 error = EPERM;
313 goto out;
314 }
315 error = ext2_dirremove(dvp, ap->a_cnp);
316 if (error == 0) {
317 ip->i_nlink--;
318 ip->i_flag |= IN_CHANGE;
319 }
320out:
321 return (error);
322}
323
324/*
325 * link vnode call
326 */
327static int
328ext2_link(ap)
329 struct vop_link_args /* {
330 struct vnode *a_tdvp;
331 struct vnode *a_vp;
332 struct componentname *a_cnp;
333 } */ *ap;
334{
335 struct vnode *vp = ap->a_vp;
336 struct vnode *tdvp = ap->a_tdvp;
337 struct componentname *cnp = ap->a_cnp;
338 struct proc *p = cnp->cn_proc;
339 struct inode *ip;
340 int error;
341
342#ifdef DIAGNOSTIC
343 if ((cnp->cn_flags & HASBUF) == 0)
344 panic("ufs_link: no name");
345#endif
346 if (tdvp->v_mount != vp->v_mount) {
347 error = EXDEV;
348 goto out2;
349 }
350 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
351 goto out2;
352 }
353 ip = VTOI(vp);
354 if ((nlink_t)ip->i_nlink >= LINK_MAX) {
355 error = EMLINK;
356 goto out1;
357 }
358 if (ip->i_flags & (IMMUTABLE | APPEND)) {
359 error = EPERM;
360 goto out1;
361 }
362 ip->i_nlink++;
363 ip->i_flag |= IN_CHANGE;
364 error = UFS_UPDATE(vp, 1);
365 if (!error)
366 error = ext2_direnter(ip, tdvp, cnp);
367 if (error) {
368 ip->i_nlink--;
369 ip->i_flag |= IN_CHANGE;
370 }
371out1:
372 if (tdvp != vp)
373 VOP_UNLOCK(vp, 0, p);
374out2:
375 return (error);
376}
377
378/*
379 * Rename system call.
380 * See comments in sys/ufs/ufs/ufs_vnops.c
381 */
382static int
383ext2_rename(ap)
384 struct vop_rename_args /* {
385 struct vnode *a_fdvp;
386 struct vnode *a_fvp;
387 struct componentname *a_fcnp;
388 struct vnode *a_tdvp;
389 struct vnode *a_tvp;
390 struct componentname *a_tcnp;
391 } */ *ap;
392{
393 struct vnode *tvp = ap->a_tvp;
394 register struct vnode *tdvp = ap->a_tdvp;
395 struct vnode *fvp = ap->a_fvp;
396 struct vnode *fdvp = ap->a_fdvp;
397 struct componentname *tcnp = ap->a_tcnp;
398 struct componentname *fcnp = ap->a_fcnp;
399 struct proc *p = fcnp->cn_proc;
400 struct inode *ip, *xp, *dp;
401 struct dirtemplate dirbuf;
402 int doingdirectory = 0, oldparent = 0, newparent = 0;
403 int error = 0;
404 u_char namlen;
405
406#ifdef DIAGNOSTIC
407 if ((tcnp->cn_flags & HASBUF) == 0 ||
408 (fcnp->cn_flags & HASBUF) == 0)
409 panic("ufs_rename: no name");
410#endif
411 /*
412 * Check for cross-device rename.
413 */
414 if ((fvp->v_mount != tdvp->v_mount) ||
415 (tvp && (fvp->v_mount != tvp->v_mount))) {
416 error = EXDEV;
417abortit:
418 if (tdvp == tvp)
419 vrele(tdvp);
420 else
421 vput(tdvp);
422 if (tvp)
423 vput(tvp);
424 vrele(fdvp);
425 vrele(fvp);
426 return (error);
427 }
428
429 if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
430 (VTOI(tdvp)->i_flags & APPEND))) {
431 error = EPERM;
432 goto abortit;
433 }
434
435 /*
436 * Check if just deleting a link name or if we've lost a race.
437 * If another process completes the same rename after we've looked
438 * up the source and have blocked looking up the target, then the
439 * source and target inodes may be identical now although the
440 * names were never linked.
441 */
442 if (fvp == tvp) {
443 if (fvp->v_type == VDIR) {
444 /*
445 * Linked directories are impossible, so we must
446 * have lost the race. Pretend that the rename
447 * completed before the lookup.
448 */
449#ifdef UFS_RENAME_DEBUG
450 printf("ufs_rename: fvp == tvp for directories\n");
451#endif
452 error = ENOENT;
453 goto abortit;
454 }
455
456 /* Release destination completely. */
457 vput(tdvp);
458 vput(tvp);
459
460 /*
461 * Delete source. There is another race now that everything
462 * is unlocked, but this doesn't cause any new complications.
463 * Relookup() may find a file that is unrelated to the
464 * original one, or it may fail. Too bad.
465 */
466 vrele(fdvp);
467 vrele(fvp);
468 fcnp->cn_flags &= ~MODMASK;
469 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
470 fcnp->cn_nameiop = DELETE;
471 VREF(fdvp);
472 error = relookup(fdvp, &fvp, fcnp);
473 if (error == 0)
474 vrele(fdvp);
475 if (fvp == NULL) {
476#ifdef UFS_RENAME_DEBUG
477 printf("ufs_rename: from name disappeared\n");
478#endif
479 return (ENOENT);
480 }
481 error = VOP_REMOVE(fdvp, fvp, fcnp);
482 if (fdvp == fvp)
483 vrele(fdvp);
484 else
485 vput(fdvp);
486 if (fvp != NULLVP)
487 vput(fvp);
488 return (error);
489 }
490 if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
491 goto abortit;
492 dp = VTOI(fdvp);
493 ip = VTOI(fvp);
494 if (ip->i_nlink >= LINK_MAX) {
495 VOP_UNLOCK(fvp, 0, p);
496 error = EMLINK;
497 goto abortit;
498 }
499 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
500 || (dp->i_flags & APPEND)) {
501 VOP_UNLOCK(fvp, 0, p);
502 error = EPERM;
503 goto abortit;
504 }
505 if ((ip->i_mode & IFMT) == IFDIR) {
506 /*
507 * Avoid ".", "..", and aliases of "." for obvious reasons.
508 */
509 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
510 dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT ||
511 (ip->i_flag & IN_RENAME)) {
512 VOP_UNLOCK(fvp, 0, p);
513 error = EINVAL;
514 goto abortit;
515 }
516 ip->i_flag |= IN_RENAME;
517 oldparent = dp->i_number;
518 doingdirectory++;
519 }
520 vrele(fdvp);
521
522 /*
523 * When the target exists, both the directory
524 * and target vnodes are returned locked.
525 */
526 dp = VTOI(tdvp);
527 xp = NULL;
528 if (tvp)
529 xp = VTOI(tvp);
530
531 /*
532 * 1) Bump link count while we're moving stuff
533 * around. If we crash somewhere before
534 * completing our work, the link count
535 * may be wrong, but correctable.
536 */
537 ip->i_nlink++;
538 ip->i_flag |= IN_CHANGE;
539 if ((error = UFS_UPDATE(fvp, 1)) != 0) {
540 VOP_UNLOCK(fvp, 0, p);
541 goto bad;
542 }
543
544 /*
545 * If ".." must be changed (ie the directory gets a new
546 * parent) then the source directory must not be in the
547 * directory heirarchy above the target, as this would
548 * orphan everything below the source directory. Also
549 * the user must have write permission in the source so
550 * as to be able to change "..". We must repeat the call
551 * to namei, as the parent directory is unlocked by the
552 * call to checkpath().
553 */
554 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
555 VOP_UNLOCK(fvp, 0, p);
556 if (oldparent != dp->i_number)
557 newparent = dp->i_number;
558 if (doingdirectory && newparent) {
559 if (error) /* write access check above */
560 goto bad;
561 if (xp != NULL)
562 vput(tvp);
563 error = ext2_checkpath(ip, dp, tcnp->cn_cred);
564 if (error)
565 goto out;
566 VREF(tdvp);
567 error = relookup(tdvp, &tvp, tcnp);
568 if (error)
569 goto out;
570 vrele(tdvp);
571 dp = VTOI(tdvp);
572 xp = NULL;
573 if (tvp)
574 xp = VTOI(tvp);
575 }
576 /*
577 * 2) If target doesn't exist, link the target
578 * to the source and unlink the source.
579 * Otherwise, rewrite the target directory
580 * entry to reference the source inode and
581 * expunge the original entry's existence.
582 */
583 if (xp == NULL) {
584 if (dp->i_dev != ip->i_dev)
585 panic("ufs_rename: EXDEV");
586 /*
587 * Account for ".." in new directory.
588 * When source and destination have the same
589 * parent we don't fool with the link count.
590 */
591 if (doingdirectory && newparent) {
592 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
593 error = EMLINK;
594 goto bad;
595 }
596 dp->i_nlink++;
597 dp->i_flag |= IN_CHANGE;
598 error = UFS_UPDATE(tdvp, 1);
599 if (error)
600 goto bad;
601 }
602 error = ext2_direnter(ip, tdvp, tcnp);
603 if (error) {
604 if (doingdirectory && newparent) {
605 dp->i_nlink--;
606 dp->i_flag |= IN_CHANGE;
607 (void)UFS_UPDATE(tdvp, 1);
608 }
609 goto bad;
610 }
611 vput(tdvp);
612 } else {
613 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
614 panic("ufs_rename: EXDEV");
615 /*
616 * Short circuit rename(foo, foo).
617 */
618 if (xp->i_number == ip->i_number)
619 panic("ufs_rename: same file");
620 /*
621 * If the parent directory is "sticky", then the user must
622 * own the parent directory, or the destination of the rename,
623 * otherwise the destination may not be changed (except by
624 * root). This implements append-only directories.
625 */
626 if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
627 tcnp->cn_cred->cr_uid != dp->i_uid &&
628 xp->i_uid != tcnp->cn_cred->cr_uid) {
629 error = EPERM;
630 goto bad;
631 }
632 /*
633 * Target must be empty if a directory and have no links
634 * to it. Also, ensure source and target are compatible
635 * (both directories, or both not directories).
636 */
637 if ((xp->i_mode&IFMT) == IFDIR) {
638 if (! ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
639 xp->i_nlink > 2) {
640 error = ENOTEMPTY;
641 goto bad;
642 }
643 if (!doingdirectory) {
644 error = ENOTDIR;
645 goto bad;
646 }
647 cache_purge(tdvp);
648 } else if (doingdirectory) {
649 error = EISDIR;
650 goto bad;
651 }
652 error = ext2_dirrewrite(dp, ip, tcnp);
653 if (error)
654 goto bad;
655 /*
656 * If the target directory is in the same
657 * directory as the source directory,
658 * decrement the link count on the parent
659 * of the target directory.
660 */
661 if (doingdirectory && !newparent) {
662 dp->i_nlink--;
663 dp->i_flag |= IN_CHANGE;
664 }
665 vput(tdvp);
666 /*
667 * Adjust the link count of the target to
668 * reflect the dirrewrite above. If this is
669 * a directory it is empty and there are
670 * no links to it, so we can squash the inode and
671 * any space associated with it. We disallowed
672 * renaming over top of a directory with links to
673 * it above, as the remaining link would point to
674 * a directory without "." or ".." entries.
675 */
676 xp->i_nlink--;
677 if (doingdirectory) {
678 if (--xp->i_nlink != 0)
679 panic("ufs_rename: linked directory");
680 error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC,
681 tcnp->cn_cred, tcnp->cn_proc);
682 }
683 xp->i_flag |= IN_CHANGE;
684 vput(tvp);
685 xp = NULL;
686 }
687
688 /*
689 * 3) Unlink the source.
690 */
691 fcnp->cn_flags &= ~MODMASK;
692 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
693 VREF(fdvp);
694 error = relookup(fdvp, &fvp, fcnp);
695 if (error == 0)
696 vrele(fdvp);
697 if (fvp != NULL) {
698 xp = VTOI(fvp);
699 dp = VTOI(fdvp);
700 } else {
701 /*
702 * From name has disappeared.
703 */
704 if (doingdirectory)
705 panic("ufs_rename: lost dir entry");
706 vrele(ap->a_fvp);
707 return (0);
708 }
709 /*
710 * Ensure that the directory entry still exists and has not
711 * changed while the new name has been entered. If the source is
712 * a file then the entry may have been unlinked or renamed. In
713 * either case there is no further work to be done. If the source
714 * is a directory then it cannot have been rmdir'ed; its link
715 * count of three would cause a rmdir to fail with ENOTEMPTY.
716 * The IN_RENAME flag ensures that it cannot be moved by another
717 * rename.
718 */
719 if (xp != ip) {
720 if (doingdirectory)
721 panic("ufs_rename: lost dir entry");
722 } else {
723 /*
724 * If the source is a directory with a
725 * new parent, the link count of the old
726 * parent directory must be decremented
727 * and ".." set to point to the new parent.
728 */
729 if (doingdirectory && newparent) {
730 dp->i_nlink--;
731 dp->i_flag |= IN_CHANGE;
732 error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
733 sizeof (struct dirtemplate), (off_t)0,
734 UIO_SYSSPACE, IO_NODELOCKED,
735 tcnp->cn_cred, (int *)0, (struct proc *)0);
736 if (error == 0) {
737 /* Like ufs little-endian: */
738 namlen = dirbuf.dotdot_type;
739 if (namlen != 2 ||
740 dirbuf.dotdot_name[0] != '.' ||
741 dirbuf.dotdot_name[1] != '.') {
742 ufs_dirbad(xp, (doff_t)12,
743 "rename: mangled dir");
744 } else {
745 dirbuf.dotdot_ino = newparent;
746 (void) vn_rdwr(UIO_WRITE, fvp,
747 (caddr_t)&dirbuf,
748 sizeof (struct dirtemplate),
749 (off_t)0, UIO_SYSSPACE,
750 IO_NODELOCKED|IO_SYNC,
751 tcnp->cn_cred, (int *)0,
752 (struct proc *)0);
753 cache_purge(fdvp);
754 }
755 }
756 }
757 error = ext2_dirremove(fdvp, fcnp);
758 if (!error) {
759 xp->i_nlink--;
760 xp->i_flag |= IN_CHANGE;
761 }
762 xp->i_flag &= ~IN_RENAME;
763 }
764 if (dp)
765 vput(fdvp);
766 if (xp)
767 vput(fvp);
768 vrele(ap->a_fvp);
769 return (error);
770
771bad:
772 if (xp)
773 vput(ITOV(xp));
774 vput(ITOV(dp));
775out:
776 if (doingdirectory)
777 ip->i_flag &= ~IN_RENAME;
778 if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {
779 ip->i_nlink--;
780 ip->i_flag |= IN_CHANGE;
781 ip->i_flag &= ~IN_RENAME;
782 vput(fvp);
783 } else
784 vrele(fvp);
785 return (error);
786}
787
788/*
789 * Mkdir system call
790 */
791static int
792ext2_mkdir(ap)
793 struct vop_mkdir_args /* {
794 struct vnode *a_dvp;
795 struct vnode **a_vpp;
796 struct componentname *a_cnp;
797 struct vattr *a_vap;
798 } */ *ap;
799{
800 register struct vnode *dvp = ap->a_dvp;
801 register struct vattr *vap = ap->a_vap;
802 register struct componentname *cnp = ap->a_cnp;
803 register struct inode *ip, *dp;
804 struct vnode *tvp;
805 struct dirtemplate dirtemplate, *dtp;
806 int error, dmode;
807
808#ifdef DIAGNOSTIC
809 if ((cnp->cn_flags & HASBUF) == 0)
810 panic("ufs_mkdir: no name");
811#endif
812 dp = VTOI(dvp);
813 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
814 error = EMLINK;
815 goto out;
816 }
817 dmode = vap->va_mode & 0777;
818 dmode |= IFDIR;
819 /*
820 * Must simulate part of ext2_makeinode here to acquire the inode,
821 * but not have it entered in the parent directory. The entry is
822 * made later after writing "." and ".." entries.
823 */
824 error = UFS_VALLOC(dvp, dmode, cnp->cn_cred, &tvp);
825 if (error)
826 goto out;
827 ip = VTOI(tvp);
828 ip->i_gid = dp->i_gid;
829#ifdef SUIDDIR
830 {
831#ifdef QUOTA
832 struct ucred ucred, *ucp;
833 ucp = cnp->cn_cred;
834#endif I
835 /*
836 * if we are hacking owners here, (only do this where told to)
837 * and we are not giving it TOO root, (would subvert quotas)
838 * then go ahead and give it to the other user.
839 * The new directory also inherits the SUID bit.
840 * If user's UID and dir UID are the same,
841 * 'give it away' so that the SUID is still forced on.
842 */
843 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
844 (dp->i_mode & ISUID) && dp->i_uid) {
845 dmode |= ISUID;
846 ip->i_uid = dp->i_uid;
847#ifdef QUOTA
848 if (dp->i_uid != cnp->cn_cred->cr_uid) {
849 /*
850 * make sure the correct user gets charged
851 * for the space.
852 * Make a dummy credential for the victim.
853 * XXX This seems to never be accessed out of
854 * our context so a stack variable is ok.
855 */
856 ucred.cr_ref = 1;
857 ucred.cr_uid = ip->i_uid;
858 ucred.cr_ngroups = 1;
859 ucred.cr_groups[0] = dp->i_gid;
860 ucp = &ucred;
861 }
862#endif I
863 } else {
864 ip->i_uid = cnp->cn_cred->cr_uid;
865 }
866#ifdef QUOTA
867 if ((error = getinoquota(ip)) ||
868 (error = chkiq(ip, 1, ucp, 0))) {
869 UFS_VFREE(tvp, ip->i_number, dmode);
870 vput(tvp);
871 return (error);
872 }
873#endif
874 }
875#else
876 ip->i_uid = cnp->cn_cred->cr_uid;
877#ifdef QUOTA
878 if ((error = getinoquota(ip)) ||
879 (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
880 UFS_VFREE(tvp, ip->i_number, dmode);
881 vput(tvp);
882 return (error);
883 }
884#endif
885#endif
886 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
887 ip->i_mode = dmode;
888 tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
889 ip->i_nlink = 2;
890 if (cnp->cn_flags & ISWHITEOUT)
891 ip->i_flags |= UF_OPAQUE;
892 error = UFS_UPDATE(tvp, 1);
893
894 /*
895 * Bump link count in parent directory
896 * to reflect work done below. Should
897 * be done before reference is created
898 * so reparation is possible if we crash.
899 */
900 dp->i_nlink++;
901 dp->i_flag |= IN_CHANGE;
902 error = UFS_UPDATE(dvp, 1);
903 if (error)
904 goto bad;
905
906 /* Initialize directory with "." and ".." from static template. */
907 if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
908 EXT2_FEATURE_INCOMPAT_FILETYPE))
909 dtp = &mastertemplate;
910 else
911 dtp = &omastertemplate;
912 dirtemplate = *dtp;
913 dirtemplate.dot_ino = ip->i_number;
914 dirtemplate.dotdot_ino = dp->i_number;
915 /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE
916 * so let's just redefine it - for this function only
917 */
918#undef DIRBLKSIZ
919#define DIRBLKSIZ VTOI(dvp)->i_e2fs->s_blocksize
920 dirtemplate.dotdot_reclen = DIRBLKSIZ - 12;
921 error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
922 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
923 IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0);
924 if (error) {
925 dp->i_nlink--;
926 dp->i_flag |= IN_CHANGE;
927 goto bad;
928 }
929 if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
930 panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */
931 else {
932 ip->i_size = DIRBLKSIZ;
933 ip->i_flag |= IN_CHANGE;
934 }
935
936 /* Directory set up, now install its entry in the parent directory. */
937 error = ext2_direnter(ip, dvp, cnp);
938 if (error) {
939 dp->i_nlink--;
940 dp->i_flag |= IN_CHANGE;
941 }
942bad:
943 /*
944 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
945 * for us because we set the link count to 0.
946 */
947 if (error) {
948 ip->i_nlink = 0;
949 ip->i_flag |= IN_CHANGE;
950 vput(tvp);
951 } else
952 *ap->a_vpp = tvp;
953out:
954 return (error);
955#undef DIRBLKSIZ
956#define DIRBLKSIZ DEV_BSIZE
957}
958
959/*
960 * Rmdir system call.
961 */
962static int
963ext2_rmdir(ap)
964 struct vop_rmdir_args /* {
965 struct vnode *a_dvp;
966 struct vnode *a_vp;
967 struct componentname *a_cnp;
968 } */ *ap;
969{
970 struct vnode *vp = ap->a_vp;
971 struct vnode *dvp = ap->a_dvp;
972 struct componentname *cnp = ap->a_cnp;
973 struct proc *p = cnp->cn_proc;
974 struct inode *ip, *dp;
975 int error;
976
977 ip = VTOI(vp);
978 dp = VTOI(dvp);
979
980 /*
981 * Verify the directory is empty (and valid).
982 * (Rmdir ".." won't be valid since
983 * ".." will contain a reference to
984 * the current directory and thus be
985 * non-empty.)
986 */
987 error = 0;
988 if (ip->i_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
989 error = ENOTEMPTY;
990 goto out;
991 }
992 if ((dp->i_flags & APPEND)
993 || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
994 error = EPERM;
995 goto out;
996 }
997 /*
998 * Delete reference to directory before purging
999 * inode. If we crash in between, the directory
1000 * will be reattached to lost+found,
1001 */
1002 error = ext2_dirremove(dvp, cnp);
1003 if (error)
1004 goto out;
1005 dp->i_nlink--;
1006 dp->i_flag |= IN_CHANGE;
1007 cache_purge(dvp);
1008 VOP_UNLOCK(dvp, 0, p);
1009 /*
1010 * Truncate inode. The only stuff left
1011 * in the directory is "." and "..". The
1012 * "." reference is inconsequential since
1013 * we're quashing it. The ".." reference
1014 * has already been adjusted above. We've
1015 * removed the "." reference and the reference
1016 * in the parent directory, but there may be
1017 * other hard links so decrement by 2 and
1018 * worry about them later.
1019 */
1020 ip->i_nlink -= 2;
1021 error = UFS_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, p);
1022 cache_purge(ITOV(ip));
1023 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
1024out:
1025 return (error);
1026}
1027
1028/*
1029 * symlink -- make a symbolic link
1030 */
1031static int
1032ext2_symlink(ap)
1033 struct vop_symlink_args /* {
1034 struct vnode *a_dvp;
1035 struct vnode **a_vpp;
1036 struct componentname *a_cnp;
1037 struct vattr *a_vap;
1038 char *a_target;
1039 } */ *ap;
1040{
1041 register struct vnode *vp, **vpp = ap->a_vpp;
1042 register struct inode *ip;
1043 int len, error;
1044
1045 error = ext2_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1046 vpp, ap->a_cnp);
1047 if (error)
1048 return (error);
1049 vp = *vpp;
1050 len = strlen(ap->a_target);
1051 if (len < vp->v_mount->mnt_maxsymlinklen) {
1052 ip = VTOI(vp);
1053 bcopy(ap->a_target, (char *)ip->i_shortlink, len);
1054 ip->i_size = len;
1055 ip->i_flag |= IN_CHANGE | IN_UPDATE;
1056 } else
1057 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1058 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
1059 (struct proc *)0);
1060 if (error)
1061 vput(vp);
1062 return (error);
1063}
1064
1065/*
1066 * Allocate a new inode.
1067 */
1068static int
1069ext2_makeinode(mode, dvp, vpp, cnp)
1070 int mode;
1071 struct vnode *dvp;
1072 struct vnode **vpp;
1073 struct componentname *cnp;
1074{
1075 register struct inode *ip, *pdir;
1076 struct vnode *tvp;
1077 int error;
1078
1079 pdir = VTOI(dvp);
1080#ifdef DIAGNOSTIC
1081 if ((cnp->cn_flags & HASBUF) == 0)
1082 panic("ext2_makeinode: no name");
1083#endif
1084 *vpp = NULL;
1085 if ((mode & IFMT) == 0)
1086 mode |= IFREG;
1087
1088 error = UFS_VALLOC(dvp, mode, cnp->cn_cred, &tvp);
1089 if (error) {
1090 return (error);
1091 }
1092 ip = VTOI(tvp);
1093 ip->i_gid = pdir->i_gid;
1094#ifdef SUIDDIR
1095 {
1096#ifdef QUOTA
1097 struct ucred ucred, *ucp;
1098 ucp = cnp->cn_cred;
1099#endif I
1100 /*
1101 * if we are
1102 * not the owner of the directory,
1103 * and we are hacking owners here, (only do this where told to)
1104 * and we are not giving it TOO root, (would subvert quotas)
1105 * then go ahead and give it to the other user.
1106 * Note that this drops off the execute bits for security.
1107 */
1108 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
1109 (pdir->i_mode & ISUID) &&
1110 (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
1111 ip->i_uid = pdir->i_uid;
1112 mode &= ~07111;
1113#ifdef QUOTA
1114 /*
1115 * make sure the correct user gets charged
1116 * for the space.
1117 * Quickly knock up a dummy credential for the victim.
1118 * XXX This seems to never be accessed out of our
1119 * context so a stack variable is ok.
1120 */
1121 ucred.cr_ref = 1;
1122 ucred.cr_uid = ip->i_uid;
1123 ucred.cr_ngroups = 1;
1124 ucred.cr_groups[0] = pdir->i_gid;
1125 ucp = &ucred;
1126#endif I
1127 } else {
1128 ip->i_uid = cnp->cn_cred->cr_uid;
1129 }
1130
1131#ifdef QUOTA
1132 if ((error = getinoquota(ip)) ||
1133 (error = chkiq(ip, 1, ucp, 0))) {
1134 UFS_VFREE(tvp, ip->i_number, mode);
1135 vput(tvp);
1136 return (error);
1137 }
1138#endif
1139 }
1140#else
1141 ip->i_uid = cnp->cn_cred->cr_uid;
1142#ifdef QUOTA
1143 if ((error = getinoquota(ip)) ||
1144 (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
1145 UFS_VFREE(tvp, ip->i_number, mode);
1146 vput(tvp);
1147 return (error);
1148 }
1149#endif
1150#endif
1151 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1152 ip->i_mode = mode;
1153 tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
1154 ip->i_nlink = 1;
1155 if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) &&
1156 suser_xxx(cnp->cn_cred, 0, PRISON_ROOT))
1157 ip->i_mode &= ~ISGID;
1158
1159 if (cnp->cn_flags & ISWHITEOUT)
1160 ip->i_flags |= UF_OPAQUE;
1161
1162 /*
1163 * Make sure inode goes to disk before directory entry.
1164 */
1165 error = UFS_UPDATE(tvp, 1);
1166 if (error)
1167 goto bad;
1168 error = ext2_direnter(ip, dvp, cnp);
1169 if (error)
1170 goto bad;
1171
1172 *vpp = tvp;
1173 return (0);
1174
1175bad:
1176 /*
1177 * Write error occurred trying to update the inode
1178 * or the directory so must deallocate the inode.
1179 */
1180 ip->i_nlink = 0;
1181 ip->i_flag |= IN_CHANGE;
1182 vput(tvp);
1183 return (error);
1184}