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