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