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