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