Annotation of src/sys/ufs/ext2fs/ext2fs_vnops.c, Revision 1.42
1.42 ! jdolecek 1: /* $NetBSD: ext2fs_vnops.c,v 1.41 2002/09/22 19:32:56 jdolecek Exp $ */
1.1 bouyer 2:
3: /*
4: * Copyright (c) 1997 Manuel Bouyer.
5: * Copyright (c) 1982, 1986, 1989, 1993
6: * The Regents of the University of California. All rights reserved.
7: * (c) UNIX System Laboratories, Inc.
8: * All or some portions of this file are derived from material licensed
9: * to the University of California by American Telephone and Telegraph
10: * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11: * the permission of UNIX System Laboratories, Inc.
12: *
13: * Redistribution and use in source and binary forms, with or without
14: * modification, are permitted provided that the following conditions
15: * are met:
16: * 1. Redistributions of source code must retain the above copyright
17: * notice, this list of conditions and the following disclaimer.
18: * 2. Redistributions in binary form must reproduce the above copyright
19: * notice, this list of conditions and the following disclaimer in the
20: * documentation and/or other materials provided with the distribution.
21: * 3. All advertising materials mentioning features or use of this software
22: * must display the following acknowledgement:
23: * This product includes software developed by the University of
24: * California, Berkeley and its contributors.
25: * 4. Neither the name of the University nor the names of its contributors
26: * may be used to endorse or promote products derived from this software
27: * without specific prior written permission.
28: *
29: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39: * SUCH DAMAGE.
40: *
41: * @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94
42: * Modified for ext2fs by Manuel Bouyer.
43: */
1.40 lukem 44:
45: #include <sys/cdefs.h>
1.42 ! jdolecek 46: __KERNEL_RCSID(0, "$NetBSD: ext2fs_vnops.c,v 1.41 2002/09/22 19:32:56 jdolecek Exp $");
1.7 mrg 47:
1.1 bouyer 48: #include <sys/param.h>
49: #include <sys/systm.h>
50: #include <sys/resourcevar.h>
51: #include <sys/kernel.h>
52: #include <sys/file.h>
53: #include <sys/stat.h>
54: #include <sys/buf.h>
55: #include <sys/proc.h>
56: #include <sys/mount.h>
57: #include <sys/namei.h>
58: #include <sys/vnode.h>
59: #include <sys/lockf.h>
60: #include <sys/malloc.h>
1.14 thorpej 61: #include <sys/pool.h>
1.1 bouyer 62: #include <sys/signalvar.h>
63:
64: #include <miscfs/fifofs/fifo.h>
65: #include <miscfs/genfs/genfs.h>
66: #include <miscfs/specfs/specdev.h>
67:
68: #include <ufs/ufs/inode.h>
69: #include <ufs/ufs/ufs_extern.h>
70: #include <ufs/ufs/ufsmount.h>
71:
72: #include <ufs/ext2fs/ext2fs.h>
73: #include <ufs/ext2fs/ext2fs_extern.h>
74: #include <ufs/ext2fs/ext2fs_dir.h>
75:
1.32 tsutsui 76: extern int prtactive;
1.1 bouyer 77:
78: static int ext2fs_chmod
79: __P((struct vnode *, int, struct ucred *, struct proc *));
80: static int ext2fs_chown
81: __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *));
82:
83: union _qcvt {
84: int64_t qcvt;
85: int32_t val[2];
86: };
87: #define SETHIGH(q, h) { \
88: union _qcvt tmp; \
89: tmp.qcvt = (q); \
90: tmp.val[_QUAD_HIGHWORD] = (h); \
91: (q) = tmp.qcvt; \
92: }
93: #define SETLOW(q, l) { \
94: union _qcvt tmp; \
95: tmp.qcvt = (q); \
96: tmp.val[_QUAD_LOWWORD] = (l); \
97: (q) = tmp.qcvt; \
98: }
99:
100: /*
101: * Create a regular file
102: */
103: int
104: ext2fs_create(v)
105: void *v;
106: {
107: struct vop_create_args /* {
108: struct vnode *a_dvp;
109: struct vnode **a_vpp;
110: struct componentname *a_cnp;
111: struct vattr *a_vap;
112: } */ *ap = v;
1.42 ! jdolecek 113: int error;
! 114:
! 115: error =
! 116: ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
! 117: ap->a_dvp, ap->a_vpp, ap->a_cnp);
! 118:
! 119: if (error)
! 120: return (error);
! 121: VN_KNOTE(ap->a_dvp, NOTE_WRITE);
! 122: return (0);
1.1 bouyer 123: }
124:
125: /*
126: * Mknod vnode call
127: */
128: /* ARGSUSED */
129: int
130: ext2fs_mknod(v)
131: void *v;
132: {
133: struct vop_mknod_args /* {
134: struct vnode *a_dvp;
135: struct vnode **a_vpp;
136: struct componentname *a_cnp;
137: struct vattr *a_vap;
138: } */ *ap = v;
1.25 augustss 139: struct vattr *vap = ap->a_vap;
140: struct vnode **vpp = ap->a_vpp;
141: struct inode *ip;
1.1 bouyer 142: int error;
1.34 assar 143: struct mount *mp;
144: ino_t ino;
1.1 bouyer 145:
1.8 fvdl 146: if ((error = ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
147: ap->a_dvp, vpp, ap->a_cnp)) != 0)
1.1 bouyer 148: return (error);
1.42 ! jdolecek 149: VN_KNOTE(ap->a_dvp, NOTE_WRITE);
1.1 bouyer 150: ip = VTOI(*vpp);
1.34 assar 151: mp = (*vpp)->v_mount;
152: ino = ip->i_number;
1.1 bouyer 153: ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
154: if (vap->va_rdev != VNOVAL) {
155: /*
156: * Want to be able to use this to make badblock
157: * inodes, so don't truncate the dev number.
158: */
1.4 bouyer 159: ip->i_din.e2fs_din.e2di_rdev = h2fs32(vap->va_rdev);
1.1 bouyer 160: }
161: /*
162: * Remove inode so that it will be reloaded by VFS_VGET and
163: * checked to see if it is an alias of an existing entry in
164: * the inode cache.
165: */
166: vput(*vpp);
167: (*vpp)->v_type = VNON;
168: vgone(*vpp);
1.34 assar 169: error = VFS_VGET(mp, ino, vpp);
170: if (error != 0) {
171: *vpp = NULL;
172: return (error);
173: }
1.1 bouyer 174: return (0);
175: }
176:
177: /*
178: * Open called.
179: *
180: * Just check the APPEND flag.
181: */
182: /* ARGSUSED */
183: int
184: ext2fs_open(v)
185: void *v;
186: {
187: struct vop_open_args /* {
188: struct vnode *a_vp;
189: int a_mode;
190: struct ucred *a_cred;
191: struct proc *a_p;
192: } */ *ap = v;
193:
194: /*
195: * Files marked append-only must be opened for appending.
196: */
197: if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
198: (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
199: return (EPERM);
200: return (0);
201: }
202:
203: int
204: ext2fs_access(v)
205: void *v;
206: {
207: struct vop_access_args /* {
208: struct vnode *a_vp;
209: int a_mode;
210: struct ucred *a_cred;
211: struct proc *a_p;
212: } */ *ap = v;
1.8 fvdl 213: struct vnode *vp = ap->a_vp;
214: struct inode *ip = VTOI(vp);
1.1 bouyer 215: mode_t mode = ap->a_mode;
216:
1.8 fvdl 217: /*
218: * Disallow write attempts on read-only file systems;
219: * unless the file is a socket, fifo, or a block or
220: * character device resident on the file system.
221: */
222: if (mode & VWRITE) {
223: switch (vp->v_type) {
224: case VDIR:
225: case VLNK:
226: case VREG:
227: if (vp->v_mount->mnt_flag & MNT_RDONLY)
228: return (EROFS);
229: break;
230: default:
231: break;
232: }
1.1 bouyer 233: }
234:
235: /* If immutable bit set, nobody gets to write it. */
236: if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
237: return (EPERM);
238:
239: return (vaccess(vp->v_type, ip->i_e2fs_mode & ALLPERMS,
240: ip->i_e2fs_uid, ip->i_e2fs_gid, mode, ap->a_cred));
241: }
242:
243: /* ARGSUSED */
244: int
245: ext2fs_getattr(v)
246: void *v;
247: {
248: struct vop_getattr_args /* {
249: struct vnode *a_vp;
250: struct vattr *a_vap;
251: struct ucred *a_cred;
252: struct proc *a_p;
253: } */ *ap = v;
1.25 augustss 254: struct vnode *vp = ap->a_vp;
255: struct inode *ip = VTOI(vp);
256: struct vattr *vap = ap->a_vap;
1.1 bouyer 257: struct timespec ts;
258:
259: TIMEVAL_TO_TIMESPEC(&time, &ts);
260: EXT2FS_ITIMES(ip, &ts, &ts, &ts);
261: /*
262: * Copy from inode table
263: */
264: vap->va_fsid = ip->i_dev;
265: vap->va_fileid = ip->i_number;
266: vap->va_mode = ip->i_e2fs_mode & ALLPERMS;
267: vap->va_nlink = ip->i_e2fs_nlink;
268: vap->va_uid = ip->i_e2fs_uid;
269: vap->va_gid = ip->i_e2fs_gid;
1.4 bouyer 270: vap->va_rdev = (dev_t)fs2h32(ip->i_din.e2fs_din.e2di_rdev);
1.37 chs 271: vap->va_size = vp->v_size;
1.1 bouyer 272: vap->va_atime.tv_sec = ip->i_e2fs_atime;
273: vap->va_atime.tv_nsec = 0;
274: vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
275: vap->va_mtime.tv_nsec = 0;
276: vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
277: vap->va_ctime.tv_nsec = 0;
278: #ifdef EXT2FS_SYSTEM_FLAGS
279: vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
280: vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
281: #else
282: vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
283: vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
284: #endif
285: vap->va_gen = ip->i_e2fs_gen;
286: /* this doesn't belong here */
287: if (vp->v_type == VBLK)
288: vap->va_blocksize = BLKDEV_IOSIZE;
289: else if (vp->v_type == VCHR)
290: vap->va_blocksize = MAXBSIZE;
291: else
292: vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
1.8 fvdl 293: vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock);
1.1 bouyer 294: vap->va_type = vp->v_type;
295: vap->va_filerev = ip->i_modrev;
296: return (0);
297: }
298:
299: /*
300: * Set attribute vnode op. called from several syscalls
301: */
302: int
303: ext2fs_setattr(v)
304: void *v;
305: {
306: struct vop_setattr_args /* {
307: struct vnode *a_vp;
308: struct vattr *a_vap;
309: struct ucred *a_cred;
310: struct proc *a_p;
311: } */ *ap = v;
1.8 fvdl 312: struct vattr *vap = ap->a_vap;
313: struct vnode *vp = ap->a_vp;
314: struct inode *ip = VTOI(vp);
315: struct ucred *cred = ap->a_cred;
316: struct proc *p = ap->a_p;
1.1 bouyer 317: int error;
318:
319: /*
320: * Check for unsettable attributes.
321: */
1.5 christos 322: if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
1.24 thorpej 323: (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
324: (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
325: ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
1.1 bouyer 326: return (EINVAL);
327: }
328: if (vap->va_flags != VNOVAL) {
1.8 fvdl 329: if (vp->v_mount->mnt_flag & MNT_RDONLY)
330: return (EROFS);
1.1 bouyer 331: if (cred->cr_uid != ip->i_e2fs_uid &&
332: (error = suser(cred, &p->p_acflag)))
333: return (error);
334: #ifdef EXT2FS_SYSTEM_FLAGS
335: if (cred->cr_uid == 0) {
1.24 thorpej 336: if ((ip->i_e2fs_flags &
337: (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0)
1.1 bouyer 338: return (EPERM);
339: ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
1.24 thorpej 340: ip->i_e2fs_flags |=
341: (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
342: (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE : 0;
343: } else
1.1 bouyer 344: return (EPERM);
345: #else
346: ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
1.24 thorpej 347: ip->i_e2fs_flags |=
348: (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
349: (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE : 0;
1.1 bouyer 350: #endif
351: ip->i_flag |= IN_CHANGE;
352: if (vap->va_flags & (IMMUTABLE | APPEND))
353: return (0);
354: }
355: if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
356: return (EPERM);
357: /*
358: * Go through the fields and update iff not VNOVAL.
359: */
360: if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
1.8 fvdl 361: if (vp->v_mount->mnt_flag & MNT_RDONLY)
362: return (EROFS);
1.1 bouyer 363: error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
364: if (error)
365: return (error);
366: }
367: if (vap->va_size != VNOVAL) {
1.8 fvdl 368: /*
369: * Disallow write attempts on read-only file systems;
370: * unless the file is a socket, fifo, or a block or
371: * character device resident on the file system.
372: */
373: switch (vp->v_type) {
374: case VDIR:
1.1 bouyer 375: return (EISDIR);
1.8 fvdl 376: case VLNK:
377: case VREG:
378: if (vp->v_mount->mnt_flag & MNT_RDONLY)
379: return (EROFS);
380: default:
381: break;
382: }
1.1 bouyer 383: error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p);
384: if (error)
385: return (error);
386: }
387: ip = VTOI(vp);
388: if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
1.8 fvdl 389: if (vp->v_mount->mnt_flag & MNT_RDONLY)
390: return (EROFS);
1.1 bouyer 391: if (cred->cr_uid != ip->i_e2fs_uid &&
392: (error = suser(cred, &p->p_acflag)) &&
393: ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
394: (error = VOP_ACCESS(vp, VWRITE, cred, p))))
395: return (error);
396: if (vap->va_atime.tv_sec != VNOVAL)
397: if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
398: ip->i_flag |= IN_ACCESS;
399: if (vap->va_mtime.tv_sec != VNOVAL)
400: ip->i_flag |= IN_CHANGE | IN_UPDATE;
1.26 perseant 401: error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime,
402: UPDATE_WAIT);
1.1 bouyer 403: if (error)
404: return (error);
405: }
406: error = 0;
1.8 fvdl 407: if (vap->va_mode != (mode_t)VNOVAL) {
408: if (vp->v_mount->mnt_flag & MNT_RDONLY)
409: return (EROFS);
1.1 bouyer 410: error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p);
1.8 fvdl 411: }
1.42 ! jdolecek 412: VN_KNOTE(vp, NOTE_ATTRIB);
1.1 bouyer 413: return (error);
414: }
415:
416: /*
417: * Change the mode on a file.
418: * Inode must be locked before calling.
419: */
420: static int
421: ext2fs_chmod(vp, mode, cred, p)
1.25 augustss 422: struct vnode *vp;
423: int mode;
424: struct ucred *cred;
1.1 bouyer 425: struct proc *p;
426: {
1.25 augustss 427: struct inode *ip = VTOI(vp);
1.1 bouyer 428: int error;
429:
430: if (cred->cr_uid != ip->i_e2fs_uid &&
431: (error = suser(cred, &p->p_acflag)))
432: return (error);
433: if (cred->cr_uid) {
434: if (vp->v_type != VDIR && (mode & S_ISTXT))
435: return (EFTYPE);
436: if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
437: return (EPERM);
438: }
439: ip->i_e2fs_mode &= ~ALLPERMS;
440: ip->i_e2fs_mode |= (mode & ALLPERMS);
441: ip->i_flag |= IN_CHANGE;
442: return (0);
443: }
444:
445: /*
446: * Perform chown operation on inode ip;
447: * inode must be locked prior to call.
448: */
449: static int
450: ext2fs_chown(vp, uid, gid, cred, p)
1.25 augustss 451: struct vnode *vp;
1.1 bouyer 452: uid_t uid;
453: gid_t gid;
454: struct ucred *cred;
455: struct proc *p;
456: {
1.25 augustss 457: struct inode *ip = VTOI(vp);
1.1 bouyer 458: uid_t ouid;
459: gid_t ogid;
460: int error = 0;
461:
462: if (uid == (uid_t)VNOVAL)
463: uid = ip->i_e2fs_uid;
464: if (gid == (gid_t)VNOVAL)
465: gid = ip->i_e2fs_gid;
466: /*
467: * If we don't own the file, are trying to change the owner
468: * of the file, or are not a member of the target group,
469: * the caller must be superuser or the call fails.
470: */
471: if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
472: (gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
473: (error = suser(cred, &p->p_acflag)))
474: return (error);
475: ogid = ip->i_e2fs_gid;
476: ouid = ip->i_e2fs_uid;
477:
478: ip->i_e2fs_gid = gid;
479: ip->i_e2fs_uid = uid;
480: if (ouid != uid || ogid != gid)
481: ip->i_flag |= IN_CHANGE;
482: if (ouid != uid && cred->cr_uid != 0)
483: ip->i_e2fs_mode &= ~ISUID;
484: if (ogid != gid && cred->cr_uid != 0)
485: ip->i_e2fs_mode &= ~ISGID;
486: return (0);
487: }
488:
489: int
490: ext2fs_remove(v)
491: void *v;
492: {
493: struct vop_remove_args /* {
494: struct vnode *a_dvp;
495: struct vnode *a_vp;
496: struct componentname *a_cnp;
497: } */ *ap = v;
1.8 fvdl 498: struct inode *ip;
499: struct vnode *vp = ap->a_vp;
500: struct vnode *dvp = ap->a_dvp;
1.1 bouyer 501: int error;
502:
503: ip = VTOI(vp);
1.2 fvdl 504: if (vp->v_type == VDIR ||
505: (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
1.1 bouyer 506: (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
507: error = EPERM;
508: goto out;
509: }
510: error = ext2fs_dirremove(dvp, ap->a_cnp);
511: if (error == 0) {
512: ip->i_e2fs_nlink--;
513: ip->i_flag |= IN_CHANGE;
514: }
515: out:
1.42 ! jdolecek 516: VN_KNOTE(vp, NOTE_DELETE);
! 517: VN_KNOTE(dvp, NOTE_WRITE);
1.1 bouyer 518: if (dvp == vp)
519: vrele(vp);
520: else
521: vput(vp);
522: vput(dvp);
523: return (error);
524: }
525:
526: /*
527: * link vnode call
528: */
529: int
530: ext2fs_link(v)
531: void *v;
532: {
533: struct vop_link_args /* {
534: struct vnode *a_dvp;
535: struct vnode *a_vp;
536: struct componentname *a_cnp;
537: } */ *ap = v;
1.8 fvdl 538: struct vnode *dvp = ap->a_dvp;
539: struct vnode *vp = ap->a_vp;
540: struct componentname *cnp = ap->a_cnp;
541: struct inode *ip;
1.1 bouyer 542: int error;
543:
544: #ifdef DIAGNOSTIC
545: if ((cnp->cn_flags & HASBUF) == 0)
546: panic("ext2fs_link: no name");
547: #endif
548: if (vp->v_type == VDIR) {
549: VOP_ABORTOP(dvp, cnp);
550: error = EISDIR;
551: goto out2;
552: }
553: if (dvp->v_mount != vp->v_mount) {
554: VOP_ABORTOP(dvp, cnp);
555: error = EXDEV;
556: goto out2;
557: }
1.8 fvdl 558: if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) {
1.1 bouyer 559: VOP_ABORTOP(dvp, cnp);
560: goto out2;
561: }
562: ip = VTOI(vp);
563: if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
564: VOP_ABORTOP(dvp, cnp);
565: error = EMLINK;
566: goto out1;
567: }
568: if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
569: VOP_ABORTOP(dvp, cnp);
570: error = EPERM;
571: goto out1;
572: }
573: ip->i_e2fs_nlink++;
574: ip->i_flag |= IN_CHANGE;
1.26 perseant 575: error = VOP_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
1.1 bouyer 576: if (!error)
577: error = ext2fs_direnter(ip, dvp, cnp);
578: if (error) {
579: ip->i_e2fs_nlink--;
580: ip->i_flag |= IN_CHANGE;
581: }
1.29 thorpej 582: PNBUF_PUT(cnp->cn_pnbuf);
1.1 bouyer 583: out1:
584: if (dvp != vp)
1.8 fvdl 585: VOP_UNLOCK(vp, 0);
1.1 bouyer 586: out2:
1.42 ! jdolecek 587: VN_KNOTE(vp, NOTE_LINK);
! 588: VN_KNOTE(dvp, NOTE_WRITE);
1.1 bouyer 589: vput(dvp);
590: return (error);
591: }
592:
593: /*
594: * Rename system call.
595: * rename("foo", "bar");
596: * is essentially
597: * unlink("bar");
598: * link("foo", "bar");
599: * unlink("foo");
600: * but ``atomically''. Can't do full commit without saving state in the
601: * inode on disk which isn't feasible at this time. Best we can do is
602: * always guarantee the target exists.
603: *
604: * Basic algorithm is:
605: *
606: * 1) Bump link count on source while we're linking it to the
607: * target. This also ensure the inode won't be deleted out
608: * from underneath us while we work (it may be truncated by
609: * a concurrent `trunc' or `open' for creation).
610: * 2) Link source to destination. If destination already exists,
611: * delete it first.
612: * 3) Unlink source reference to inode if still around. If a
613: * directory was moved and the parent of the destination
614: * is different from the source, patch the ".." entry in the
615: * directory.
616: */
617: int
618: ext2fs_rename(v)
619: void *v;
620: {
621: struct vop_rename_args /* {
622: struct vnode *a_fdvp;
623: struct vnode *a_fvp;
624: struct componentname *a_fcnp;
625: struct vnode *a_tdvp;
626: struct vnode *a_tvp;
627: struct componentname *a_tcnp;
628: } */ *ap = v;
629: struct vnode *tvp = ap->a_tvp;
1.25 augustss 630: struct vnode *tdvp = ap->a_tdvp;
1.1 bouyer 631: struct vnode *fvp = ap->a_fvp;
1.8 fvdl 632: struct vnode *fdvp = ap->a_fdvp;
633: struct componentname *tcnp = ap->a_tcnp;
634: struct componentname *fcnp = ap->a_fcnp;
635: struct inode *ip, *xp, *dp;
1.1 bouyer 636: struct ext2fs_dirtemplate dirbuf;
637: int doingdirectory = 0, oldparent = 0, newparent = 0;
638: int error = 0;
639: u_char namlen;
640:
641: #ifdef DIAGNOSTIC
642: if ((tcnp->cn_flags & HASBUF) == 0 ||
643: (fcnp->cn_flags & HASBUF) == 0)
644: panic("ext2fs_rename: no name");
645: #endif
646: /*
647: * Check for cross-device rename.
648: */
649: if ((fvp->v_mount != tdvp->v_mount) ||
650: (tvp && (fvp->v_mount != tvp->v_mount))) {
651: error = EXDEV;
652: abortit:
653: VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
654: if (tdvp == tvp)
655: vrele(tdvp);
656: else
657: vput(tdvp);
658: if (tvp)
659: vput(tvp);
660: VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
661: vrele(fdvp);
662: vrele(fvp);
663: return (error);
664: }
665:
666: /*
667: * Check if just deleting a link name.
668: */
669: if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
670: (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
671: error = EPERM;
672: goto abortit;
673: }
674: if (fvp == tvp) {
675: if (fvp->v_type == VDIR) {
676: error = EINVAL;
677: goto abortit;
678: }
679:
680: /* Release destination completely. */
681: VOP_ABORTOP(tdvp, tcnp);
682: vput(tdvp);
683: vput(tvp);
684:
685: /* Delete source. */
686: vrele(fdvp);
687: vrele(fvp);
688: fcnp->cn_flags &= ~MODMASK;
689: fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
690: if ((fcnp->cn_flags & SAVESTART) == 0)
691: panic("ext2fs_rename: lost from startdir");
692: fcnp->cn_nameiop = DELETE;
693: (void) relookup(fdvp, &fvp, fcnp);
694: return (VOP_REMOVE(fdvp, fvp, fcnp));
695: }
1.8 fvdl 696: if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
1.1 bouyer 697: goto abortit;
698: dp = VTOI(fdvp);
699: ip = VTOI(fvp);
1.17 mrg 700: if ((nlink_t) ip->i_e2fs_nlink >= LINK_MAX) {
701: VOP_UNLOCK(fvp, 0);
702: error = EMLINK;
703: goto abortit;
704: }
1.1 bouyer 705: if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
706: (dp->i_e2fs_flags & EXT2_APPEND)) {
1.8 fvdl 707: VOP_UNLOCK(fvp, 0);
1.1 bouyer 708: error = EPERM;
709: goto abortit;
710: }
711: if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
1.16 bouyer 712: error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
713: if (!error && tvp)
714: error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred,
715: tcnp->cn_proc);
716: if (error) {
717: VOP_UNLOCK(fvp, 0);
718: error = EACCES;
719: goto abortit;
720: }
1.1 bouyer 721: /*
722: * Avoid ".", "..", and aliases of "." for obvious reasons.
723: */
724: if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
1.3 bouyer 725: dp == ip ||
1.16 bouyer 726: (fcnp->cn_flags&ISDOTDOT) ||
727: (tcnp->cn_flags & ISDOTDOT) ||
1.1 bouyer 728: (ip->i_flag & IN_RENAME)) {
1.8 fvdl 729: VOP_UNLOCK(fvp, 0);
1.1 bouyer 730: error = EINVAL;
731: goto abortit;
732: }
733: ip->i_flag |= IN_RENAME;
734: oldparent = dp->i_number;
735: doingdirectory++;
736: }
1.42 ! jdolecek 737: VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */
1.1 bouyer 738: vrele(fdvp);
739:
740: /*
741: * When the target exists, both the directory
742: * and target vnodes are returned locked.
743: */
744: dp = VTOI(tdvp);
745: xp = NULL;
746: if (tvp)
747: xp = VTOI(tvp);
748:
749: /*
750: * 1) Bump link count while we're moving stuff
751: * around. If we crash somewhere before
752: * completing our work, the link count
753: * may be wrong, but correctable.
754: */
755: ip->i_e2fs_nlink++;
756: ip->i_flag |= IN_CHANGE;
1.26 perseant 757: if ((error = VOP_UPDATE(fvp, NULL, NULL, UPDATE_WAIT)) != 0) {
1.8 fvdl 758: VOP_UNLOCK(fvp, 0);
1.1 bouyer 759: goto bad;
760: }
761:
762: /*
763: * If ".." must be changed (ie the directory gets a new
764: * parent) then the source directory must not be in the
1.36 wiz 765: * directory hierarchy above the target, as this would
1.1 bouyer 766: * orphan everything below the source directory. Also
767: * the user must have write permission in the source so
768: * as to be able to change "..". We must repeat the call
769: * to namei, as the parent directory is unlocked by the
770: * call to checkpath().
771: */
772: error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
1.8 fvdl 773: VOP_UNLOCK(fvp, 0);
1.1 bouyer 774: if (oldparent != dp->i_number)
775: newparent = dp->i_number;
776: if (doingdirectory && newparent) {
777: if (error) /* write access check above */
778: goto bad;
779: if (xp != NULL)
780: vput(tvp);
781: error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
782: if (error != 0)
783: goto out;
784: if ((tcnp->cn_flags & SAVESTART) == 0)
785: panic("ext2fs_rename: lost to startdir");
786: if ((error = relookup(tdvp, &tvp, tcnp)) != 0)
787: goto out;
788: dp = VTOI(tdvp);
789: xp = NULL;
790: if (tvp)
791: xp = VTOI(tvp);
792: }
793: /*
794: * 2) If target doesn't exist, link the target
795: * to the source and unlink the source.
796: * Otherwise, rewrite the target directory
797: * entry to reference the source inode and
798: * expunge the original entry's existence.
799: */
800: if (xp == NULL) {
801: if (dp->i_dev != ip->i_dev)
802: panic("rename: EXDEV");
803: /*
804: * Account for ".." in new directory.
805: * When source and destination have the same
806: * parent we don't fool with the link count.
807: */
808: if (doingdirectory && newparent) {
809: if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
810: error = EMLINK;
811: goto bad;
812: }
813: dp->i_e2fs_nlink++;
814: dp->i_flag |= IN_CHANGE;
1.26 perseant 815: if ((error = VOP_UPDATE(tdvp, NULL, NULL, UPDATE_WAIT))
816: != 0)
1.1 bouyer 817: goto bad;
818: }
819: error = ext2fs_direnter(ip, tdvp, tcnp);
820: if (error != 0) {
821: if (doingdirectory && newparent) {
822: dp->i_e2fs_nlink--;
823: dp->i_flag |= IN_CHANGE;
1.26 perseant 824: (void)VOP_UPDATE(tdvp, NULL, NULL, UPDATE_WAIT);
1.1 bouyer 825: }
826: goto bad;
827: }
1.42 ! jdolecek 828: VN_KNOTE(tdvp, NOTE_WRITE);
1.1 bouyer 829: vput(tdvp);
830: } else {
831: if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
832: panic("rename: EXDEV");
833: /*
834: * Short circuit rename(foo, foo).
835: */
836: if (xp->i_number == ip->i_number)
837: panic("rename: same file");
838: /*
839: * If the parent directory is "sticky", then the user must
840: * own the parent directory, or the destination of the rename,
841: * otherwise the destination may not be changed (except by
842: * root). This implements append-only directories.
843: */
844: if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
845: tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
846: xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
847: error = EPERM;
848: goto bad;
849: }
850: /*
851: * Target must be empty if a directory and have no links
852: * to it. Also, ensure source and target are compatible
853: * (both directories, or both not directories).
854: */
855: if ((xp->i_e2fs_mode & IFMT) == IFDIR) {
856: if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
857: xp->i_e2fs_nlink > 2) {
858: error = ENOTEMPTY;
859: goto bad;
860: }
861: if (!doingdirectory) {
862: error = ENOTDIR;
863: goto bad;
864: }
865: cache_purge(tdvp);
866: } else if (doingdirectory) {
867: error = EISDIR;
868: goto bad;
869: }
870: error = ext2fs_dirrewrite(dp, ip, tcnp);
871: if (error != 0)
872: goto bad;
873: /*
874: * If the target directory is in the same
875: * directory as the source directory,
876: * decrement the link count on the parent
877: * of the target directory.
878: */
879: if (doingdirectory && !newparent) {
880: dp->i_e2fs_nlink--;
881: dp->i_flag |= IN_CHANGE;
882: }
1.42 ! jdolecek 883: VN_KNOTE(tdvp, NOTE_WRITE);
1.1 bouyer 884: vput(tdvp);
885: /*
886: * Adjust the link count of the target to
887: * reflect the dirrewrite above. If this is
888: * a directory it is empty and there are
889: * no links to it, so we can squash the inode and
890: * any space associated with it. We disallowed
891: * renaming over top of a directory with links to
892: * it above, as the remaining link would point to
893: * a directory without "." or ".." entries.
894: */
895: xp->i_e2fs_nlink--;
896: if (doingdirectory) {
897: if (--xp->i_e2fs_nlink != 0)
898: panic("rename: linked directory");
899: error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC,
900: tcnp->cn_cred, tcnp->cn_proc);
901: }
902: xp->i_flag |= IN_CHANGE;
1.42 ! jdolecek 903: VN_KNOTE(tvp, NOTE_DELETE);
1.1 bouyer 904: vput(tvp);
905: xp = NULL;
906: }
907:
908: /*
909: * 3) Unlink the source.
910: */
911: fcnp->cn_flags &= ~MODMASK;
912: fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
913: if ((fcnp->cn_flags & SAVESTART) == 0)
914: panic("ext2fs_rename: lost from startdir");
915: (void) relookup(fdvp, &fvp, fcnp);
916: if (fvp != NULL) {
917: xp = VTOI(fvp);
918: dp = VTOI(fdvp);
919: } else {
920: /*
921: * From name has disappeared.
922: */
923: if (doingdirectory)
924: panic("ext2fs_rename: lost dir entry");
925: vrele(ap->a_fvp);
926: return (0);
927: }
928: /*
929: * Ensure that the directory entry still exists and has not
930: * changed while the new name has been entered. If the source is
931: * a file then the entry may have been unlinked or renamed. In
932: * either case there is no further work to be done. If the source
933: * is a directory then it cannot have been rmdir'ed; its link
934: * count of three would cause a rmdir to fail with ENOTEMPTY.
935: * The IRENAME flag ensures that it cannot be moved by another
936: * rename.
937: */
938: if (xp != ip) {
939: if (doingdirectory)
940: panic("ext2fs_rename: lost dir entry");
941: } else {
942: /*
943: * If the source is a directory with a
944: * new parent, the link count of the old
945: * parent directory must be decremented
946: * and ".." set to point to the new parent.
947: */
948: if (doingdirectory && newparent) {
949: dp->i_e2fs_nlink--;
950: dp->i_flag |= IN_CHANGE;
951: error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
952: sizeof (struct ext2fs_dirtemplate), (off_t)0,
953: UIO_SYSSPACE, IO_NODELOCKED,
1.12 mjacob 954: tcnp->cn_cred, (size_t *)0, (struct proc *)0);
1.1 bouyer 955: if (error == 0) {
1.22 bouyer 956: namlen = dirbuf.dotdot_namlen;
1.1 bouyer 957: if (namlen != 2 ||
958: dirbuf.dotdot_name[0] != '.' ||
959: dirbuf.dotdot_name[1] != '.') {
960: ufs_dirbad(xp, (doff_t)12,
961: "ext2fs_rename: mangled dir");
962: } else {
1.4 bouyer 963: dirbuf.dotdot_ino = h2fs32(newparent);
1.1 bouyer 964: (void) vn_rdwr(UIO_WRITE, fvp,
965: (caddr_t)&dirbuf,
966: sizeof (struct dirtemplate),
967: (off_t)0, UIO_SYSSPACE,
968: IO_NODELOCKED|IO_SYNC,
1.12 mjacob 969: tcnp->cn_cred, (size_t *)0,
1.1 bouyer 970: (struct proc *)0);
971: cache_purge(fdvp);
972: }
973: }
974: }
975: error = ext2fs_dirremove(fdvp, fcnp);
976: if (!error) {
977: xp->i_e2fs_nlink--;
978: xp->i_flag |= IN_CHANGE;
979: }
980: xp->i_flag &= ~IN_RENAME;
981: }
1.42 ! jdolecek 982: VN_KNOTE(fvp, NOTE_RENAME);
1.1 bouyer 983: if (dp)
984: vput(fdvp);
985: if (xp)
986: vput(fvp);
987: vrele(ap->a_fvp);
988: return (error);
989:
990: bad:
991: if (xp)
992: vput(ITOV(xp));
993: vput(ITOV(dp));
994: out:
995: if (doingdirectory)
996: ip->i_flag &= ~IN_RENAME;
1.8 fvdl 997: if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
1.1 bouyer 998: ip->i_e2fs_nlink--;
999: ip->i_flag |= IN_CHANGE;
1000: vput(fvp);
1001: } else
1002: vrele(fvp);
1003: return (error);
1004: }
1005:
1006: /*
1007: * Mkdir system call
1008: */
1009: int
1010: ext2fs_mkdir(v)
1011: void *v;
1012: {
1013: struct vop_mkdir_args /* {
1014: struct vnode *a_dvp;
1015: struct vnode **a_vpp;
1016: struct componentname *a_cnp;
1017: struct vattr *a_vap;
1018: } */ *ap = v;
1.25 augustss 1019: struct vnode *dvp = ap->a_dvp;
1020: struct vattr *vap = ap->a_vap;
1021: struct componentname *cnp = ap->a_cnp;
1022: struct inode *ip, *dp;
1.1 bouyer 1023: struct vnode *tvp;
1.4 bouyer 1024: struct ext2fs_dirtemplate dirtemplate;
1.1 bouyer 1025: int error, dmode;
1026:
1027: #ifdef DIAGNOSTIC
1028: if ((cnp->cn_flags & HASBUF) == 0)
1029: panic("ext2fs_mkdir: no name");
1030: #endif
1031: dp = VTOI(dvp);
1032: if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
1033: error = EMLINK;
1034: goto out;
1035: }
1036: dmode = vap->va_mode & ACCESSPERMS;
1037: dmode |= IFDIR;
1038: /*
1039: * Must simulate part of ext2fs_makeinode here to acquire the inode,
1040: * but not have it entered in the parent directory. The entry is
1041: * made later after writing "." and ".." entries.
1042: */
1043: if ((error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) != 0)
1044: goto out;
1045: ip = VTOI(tvp);
1046: ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
1047: ip->i_e2fs_gid = dp->i_e2fs_gid;
1048: ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1049: ip->i_e2fs_mode = dmode;
1050: tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
1051: ip->i_e2fs_nlink = 2;
1.26 perseant 1052: error = VOP_UPDATE(tvp, NULL, NULL, UPDATE_WAIT);
1.1 bouyer 1053:
1054: /*
1055: * Bump link count in parent directory
1056: * to reflect work done below. Should
1057: * be done before reference is created
1058: * so reparation is possible if we crash.
1059: */
1060: dp->i_e2fs_nlink++;
1061: dp->i_flag |= IN_CHANGE;
1.26 perseant 1062: if ((error = VOP_UPDATE(dvp, NULL, NULL, UPDATE_WAIT)) != 0)
1.1 bouyer 1063: goto bad;
1064:
1065: /* Initialize directory with "." and ".." from static template. */
1.13 perry 1066: memset(&dirtemplate, 0, sizeof(dirtemplate));
1.4 bouyer 1067: dirtemplate.dot_ino = h2fs32(ip->i_number);
1068: dirtemplate.dot_reclen = h2fs16(12);
1.22 bouyer 1069: dirtemplate.dot_namlen = 1;
1070: if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
1071: (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
1.23 bouyer 1072: dirtemplate.dot_type = EXT2_FT_DIR;
1.22 bouyer 1073: }
1.4 bouyer 1074: dirtemplate.dot_name[0] = '.';
1075: dirtemplate.dotdot_ino = h2fs32(dp->i_number);
1076: dirtemplate.dotdot_reclen = h2fs16(VTOI(dvp)->i_e2fs->e2fs_bsize - 12);
1.22 bouyer 1077: dirtemplate.dotdot_namlen = 2;
1078: if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
1079: (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
1.23 bouyer 1080: dirtemplate.dotdot_type = EXT2_FT_DIR;
1.22 bouyer 1081: }
1.4 bouyer 1082: dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.';
1.1 bouyer 1083: error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
1084: sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
1.12 mjacob 1085: IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (size_t *)0, (struct proc *)0);
1.1 bouyer 1086: if (error) {
1087: dp->i_e2fs_nlink--;
1088: dp->i_flag |= IN_CHANGE;
1089: goto bad;
1090: }
1091: if (VTOI(dvp)->i_e2fs->e2fs_bsize >
1092: VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
1093: panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
1094: else {
1095: ip->i_e2fs_size = VTOI(dvp)->i_e2fs->e2fs_bsize;
1096: ip->i_flag |= IN_CHANGE;
1097: }
1098:
1099: /* Directory set up, now install it's entry in the parent directory. */
1100: error = ext2fs_direnter(ip, dvp, cnp);
1101: if (error != 0) {
1102: dp->i_e2fs_nlink--;
1103: dp->i_flag |= IN_CHANGE;
1104: }
1105: bad:
1106: /*
1107: * No need to do an explicit VOP_TRUNCATE here, vrele will do this
1108: * for us because we set the link count to 0.
1109: */
1110: if (error) {
1111: ip->i_e2fs_nlink = 0;
1112: ip->i_flag |= IN_CHANGE;
1113: vput(tvp);
1.42 ! jdolecek 1114: } else {
! 1115: VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1.1 bouyer 1116: *ap->a_vpp = tvp;
1.42 ! jdolecek 1117: }
1.1 bouyer 1118: out:
1.29 thorpej 1119: PNBUF_PUT(cnp->cn_pnbuf);
1.1 bouyer 1120: vput(dvp);
1121: return (error);
1122: }
1123:
1124: /*
1125: * Rmdir system call.
1126: */
1127: int
1128: ext2fs_rmdir(v)
1129: void *v;
1130: {
1131: struct vop_rmdir_args /* {
1132: struct vnode *a_dvp;
1133: struct vnode *a_vp;
1134: struct componentname *a_cnp;
1135: } */ *ap = v;
1.25 augustss 1136: struct vnode *vp = ap->a_vp;
1137: struct vnode *dvp = ap->a_dvp;
1138: struct componentname *cnp = ap->a_cnp;
1139: struct inode *ip, *dp;
1.1 bouyer 1140: int error;
1141:
1142: ip = VTOI(vp);
1143: dp = VTOI(dvp);
1144: /*
1145: * No rmdir "." please.
1146: */
1147: if (dp == ip) {
1148: vrele(dvp);
1149: vput(vp);
1150: return (EINVAL);
1151: }
1152: /*
1153: * Verify the directory is empty (and valid).
1154: * (Rmdir ".." won't be valid since
1155: * ".." will contain a reference to
1156: * the current directory and thus be
1157: * non-empty.)
1158: */
1159: error = 0;
1160: if (ip->i_e2fs_nlink != 2 ||
1161: !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
1162: error = ENOTEMPTY;
1163: goto out;
1164: }
1165: if ((dp->i_e2fs_flags & EXT2_APPEND) ||
1166: (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
1167: error = EPERM;
1168: goto out;
1169: }
1170: /*
1171: * Delete reference to directory before purging
1172: * inode. If we crash in between, the directory
1173: * will be reattached to lost+found,
1174: */
1175: error = ext2fs_dirremove(dvp, cnp);
1176: if (error != 0)
1177: goto out;
1178: dp->i_e2fs_nlink--;
1179: dp->i_flag |= IN_CHANGE;
1.42 ! jdolecek 1180: VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1.1 bouyer 1181: cache_purge(dvp);
1182: vput(dvp);
1183: dvp = NULL;
1184: /*
1185: * Truncate inode. The only stuff left
1186: * in the directory is "." and "..". The
1187: * "." reference is inconsequential since
1188: * we're quashing it. The ".." reference
1189: * has already been adjusted above. We've
1190: * removed the "." reference and the reference
1191: * in the parent directory, but there may be
1192: * other hard links so decrement by 2 and
1193: * worry about them later.
1194: */
1195: ip->i_e2fs_nlink -= 2;
1196: error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
1197: cnp->cn_proc);
1198: cache_purge(ITOV(ip));
1199: out:
1.42 ! jdolecek 1200: VN_KNOTE(vp, NOTE_DELETE);
1.1 bouyer 1201: if (dvp)
1202: vput(dvp);
1203: vput(vp);
1204: return (error);
1205: }
1206:
1207: /*
1208: * symlink -- make a symbolic link
1209: */
1210: int
1211: ext2fs_symlink(v)
1212: void *v;
1213: {
1214: struct vop_symlink_args /* {
1215: struct vnode *a_dvp;
1216: struct vnode **a_vpp;
1217: struct componentname *a_cnp;
1218: struct vattr *a_vap;
1219: char *a_target;
1220: } */ *ap = v;
1.25 augustss 1221: struct vnode *vp, **vpp = ap->a_vpp;
1222: struct inode *ip;
1.1 bouyer 1223: int len, error;
1224:
1225: error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1226: vpp, ap->a_cnp);
1227: if (error)
1228: return (error);
1.42 ! jdolecek 1229: VN_KNOTE(ap->a_dvp, NOTE_WRITE);
1.1 bouyer 1230: vp = *vpp;
1231: len = strlen(ap->a_target);
1232: if (len < vp->v_mount->mnt_maxsymlinklen) {
1233: ip = VTOI(vp);
1.13 perry 1234: memcpy((char *)ip->i_din.e2fs_din.e2di_shortlink, ap->a_target, len);
1.1 bouyer 1235: ip->i_e2fs_size = len;
1236: ip->i_flag |= IN_CHANGE | IN_UPDATE;
1237: } else
1238: error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1.12 mjacob 1239: UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred,
1240: (size_t *)0, (struct proc *)0);
1.34 assar 1241: if (error)
1242: vput(vp);
1.1 bouyer 1243: return (error);
1244: }
1245:
1246: /*
1247: * Return target name of a symbolic link
1248: */
1249: int
1250: ext2fs_readlink(v)
1251: void *v;
1252: {
1253: struct vop_readlink_args /* {
1254: struct vnode *a_vp;
1255: struct uio *a_uio;
1256: struct ucred *a_cred;
1257: } */ *ap = v;
1.25 augustss 1258: struct vnode *vp = ap->a_vp;
1259: struct inode *ip = VTOI(vp);
1.1 bouyer 1260: int isize;
1261:
1262: isize = ip->i_e2fs_size;
1263: if (isize < vp->v_mount->mnt_maxsymlinklen ||
1264: (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) {
1265: uiomove((char *)ip->i_din.e2fs_din.e2di_shortlink, isize, ap->a_uio);
1266: return (0);
1267: }
1268: return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
1269: }
1270:
1271: /*
1272: * Advisory record locking support
1273: */
1274: int
1275: ext2fs_advlock(v)
1276: void *v;
1277: {
1278: struct vop_advlock_args /* {
1279: struct vnode *a_vp;
1280: caddr_t a_id;
1281: int a_op;
1282: struct flock *a_fl;
1283: int a_flags;
1284: } */ *ap = v;
1.25 augustss 1285: struct inode *ip = VTOI(ap->a_vp);
1.1 bouyer 1286:
1.28 jdolecek 1287: return lf_advlock(ap, &ip->i_lockf, ip->i_e2fs_size);
1.1 bouyer 1288: }
1289:
1290: /*
1291: * Initialize the vnode associated with a new inode, handle aliased
1292: * vnodes.
1293: */
1294: int
1295: ext2fs_vinit(mntp, specops, fifoops, vpp)
1296: struct mount *mntp;
1297: int (**specops) __P((void *));
1298: int (**fifoops) __P((void *));
1299: struct vnode **vpp;
1300: {
1301: struct inode *ip;
1302: struct vnode *vp, *nvp;
1303:
1304: vp = *vpp;
1305: ip = VTOI(vp);
1306: switch(vp->v_type = IFTOVT(ip->i_e2fs_mode)) {
1307: case VCHR:
1308: case VBLK:
1309: vp->v_op = specops;
1.16 bouyer 1310: if ((nvp = checkalias(vp,
1311: fs2h32(ip->i_din.e2fs_din.e2di_rdev), mntp)) != NULL) {
1.1 bouyer 1312: /*
1313: * Discard unneeded vnode, but save its inode.
1314: */
1315: nvp->v_data = vp->v_data;
1316: vp->v_data = NULL;
1.33 fvdl 1317: VOP_UNLOCK(vp, 0);
1.1 bouyer 1318: vp->v_op = spec_vnodeop_p;
1.33 fvdl 1319: vrele(vp);
1.1 bouyer 1320: vgone(vp);
1.20 wrstuden 1321: lockmgr(&nvp->v_lock, LK_EXCLUSIVE, &nvp->v_interlock);
1.1 bouyer 1322: /*
1323: * Reinitialize aliased inode.
1324: */
1325: vp = nvp;
1326: ip->i_vnode = vp;
1327: }
1328: break;
1329: case VFIFO:
1330: vp->v_op = fifoops;
1331: break;
1332: case VNON:
1333: case VBAD:
1334: case VSOCK:
1335: case VLNK:
1336: case VDIR:
1337: case VREG:
1338: break;
1339: }
1340: if (ip->i_number == ROOTINO)
1341: vp->v_flag |= VROOT;
1342: /*
1343: * Initialize modrev times
1344: */
1345: SETHIGH(ip->i_modrev, mono_time.tv_sec);
1346: SETLOW(ip->i_modrev, mono_time.tv_usec * 4294);
1347: *vpp = vp;
1348: return (0);
1349: }
1350:
1351: /*
1352: * Allocate a new inode.
1353: */
1354: int
1355: ext2fs_makeinode(mode, dvp, vpp, cnp)
1356: int mode;
1357: struct vnode *dvp;
1358: struct vnode **vpp;
1359: struct componentname *cnp;
1360: {
1.25 augustss 1361: struct inode *ip, *pdir;
1.1 bouyer 1362: struct vnode *tvp;
1363: int error;
1364:
1365: pdir = VTOI(dvp);
1366: #ifdef DIAGNOSTIC
1367: if ((cnp->cn_flags & HASBUF) == 0)
1368: panic("ext2fs_makeinode: no name");
1369: #endif
1370: *vpp = NULL;
1371: if ((mode & IFMT) == 0)
1372: mode |= IFREG;
1373:
1374: if ((error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) != 0) {
1.29 thorpej 1375: PNBUF_PUT(cnp->cn_pnbuf);
1.1 bouyer 1376: vput(dvp);
1377: return (error);
1378: }
1379: ip = VTOI(tvp);
1380: ip->i_e2fs_gid = pdir->i_e2fs_gid;
1381: ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
1382: ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1383: ip->i_e2fs_mode = mode;
1384: tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
1385: ip->i_e2fs_nlink = 1;
1386: if ((ip->i_e2fs_mode & ISGID) &&
1387: !groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
1388: suser(cnp->cn_cred, NULL))
1389: ip->i_e2fs_mode &= ~ISGID;
1390:
1391: /*
1392: * Make sure inode goes to disk before directory entry.
1393: */
1.26 perseant 1394: if ((error = VOP_UPDATE(tvp, NULL, NULL, UPDATE_WAIT)) != 0)
1.1 bouyer 1395: goto bad;
1396: error = ext2fs_direnter(ip, dvp, cnp);
1397: if (error != 0)
1398: goto bad;
1399: if ((cnp->cn_flags & SAVESTART) == 0)
1.29 thorpej 1400: PNBUF_PUT(cnp->cn_pnbuf);
1.1 bouyer 1401: vput(dvp);
1402: *vpp = tvp;
1403: return (0);
1404:
1405: bad:
1406: /*
1407: * Write error occurred trying to update the inode
1408: * or the directory so must deallocate the inode.
1409: */
1.29 thorpej 1410: PNBUF_PUT(cnp->cn_pnbuf);
1.1 bouyer 1411: vput(dvp);
1412: ip->i_e2fs_nlink = 0;
1413: ip->i_flag |= IN_CHANGE;
1414: vput(tvp);
1415: return (error);
1416: }
1417:
1418: /*
1419: * Reclaim an inode so that it can be used for other purposes.
1420: */
1421: int
1422: ext2fs_reclaim(v)
1423: void *v;
1424: {
1425: struct vop_reclaim_args /* {
1426: struct vnode *a_vp;
1427: } */ *ap = v;
1.25 augustss 1428: struct vnode *vp = ap->a_vp;
1.1 bouyer 1429: struct inode *ip;
1430:
1.8 fvdl 1431: if (prtactive && vp->v_usecount != 0)
1432: vprint("ext2fs_reclaim: pushing active", vp);
1433: /*
1434: * Remove the inode from its hash chain.
1435: */
1436: ip = VTOI(vp);
1437: ufs_ihashrem(ip);
1438: /*
1439: * Purge old data structures associated with the inode.
1440: */
1441: cache_purge(vp);
1442: if (ip->i_devvp) {
1443: vrele(ip->i_devvp);
1444: ip->i_devvp = 0;
1445: }
1.1 bouyer 1446:
1.14 thorpej 1447: pool_put(&ext2fs_inode_pool, vp->v_data);
1.1 bouyer 1448: vp->v_data = NULL;
1449: return (0);
1450: }
1451:
1452: /* Global vfs data structures for ext2fs. */
1453: int (**ext2fs_vnodeop_p) __P((void *));
1.31 jdolecek 1454: const struct vnodeopv_entry_desc ext2fs_vnodeop_entries[] = {
1.1 bouyer 1455: { &vop_default_desc, vn_default_error },
1.8 fvdl 1456: { &vop_lookup_desc, ext2fs_lookup }, /* lookup */
1457: { &vop_create_desc, ext2fs_create }, /* create */
1.1 bouyer 1458: { &vop_mknod_desc, ext2fs_mknod }, /* mknod */
1459: { &vop_open_desc, ext2fs_open }, /* open */
1460: { &vop_close_desc, ufs_close }, /* close */
1.8 fvdl 1461: { &vop_access_desc, ext2fs_access }, /* access */
1462: { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
1463: { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
1.1 bouyer 1464: { &vop_read_desc, ext2fs_read }, /* read */
1465: { &vop_write_desc, ext2fs_write }, /* write */
1.8 fvdl 1466: { &vop_lease_desc, ufs_lease_check }, /* lease */
1.1 bouyer 1467: { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */
1.21 wrstuden 1468: { &vop_fcntl_desc, ufs_fcntl }, /* fcntl */
1.1 bouyer 1469: { &vop_poll_desc, ufs_poll }, /* poll */
1.42 ! jdolecek 1470: { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */
1.8 fvdl 1471: { &vop_revoke_desc, ufs_revoke }, /* revoke */
1.1 bouyer 1472: { &vop_mmap_desc, ufs_mmap }, /* mmap */
1473: { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
1474: { &vop_seek_desc, ufs_seek }, /* seek */
1.8 fvdl 1475: { &vop_remove_desc, ext2fs_remove }, /* remove */
1.1 bouyer 1476: { &vop_link_desc, ext2fs_link }, /* link */
1.8 fvdl 1477: { &vop_rename_desc, ext2fs_rename }, /* rename */
1.1 bouyer 1478: { &vop_mkdir_desc, ext2fs_mkdir }, /* mkdir */
1479: { &vop_rmdir_desc, ext2fs_rmdir }, /* rmdir */
1.8 fvdl 1480: { &vop_symlink_desc, ext2fs_symlink }, /* symlink */
1481: { &vop_readdir_desc, ext2fs_readdir }, /* readdir */
1482: { &vop_readlink_desc, ext2fs_readlink }, /* readlink */
1.1 bouyer 1483: { &vop_abortop_desc, ufs_abortop }, /* abortop */
1.8 fvdl 1484: { &vop_inactive_desc, ext2fs_inactive }, /* inactive */
1485: { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
1.1 bouyer 1486: { &vop_lock_desc, ufs_lock }, /* lock */
1487: { &vop_unlock_desc, ufs_unlock }, /* unlock */
1488: { &vop_bmap_desc, ext2fs_bmap }, /* bmap */
1.8 fvdl 1489: { &vop_strategy_desc, ufs_strategy }, /* strategy */
1.1 bouyer 1490: { &vop_print_desc, ufs_print }, /* print */
1.8 fvdl 1491: { &vop_islocked_desc, ufs_islocked }, /* islocked */
1492: { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */
1493: { &vop_advlock_desc, ext2fs_advlock }, /* advlock */
1494: { &vop_blkatoff_desc, ext2fs_blkatoff }, /* blkatoff */
1495: { &vop_valloc_desc, ext2fs_valloc }, /* valloc */
1.1 bouyer 1496: { &vop_vfree_desc, ext2fs_vfree }, /* vfree */
1.8 fvdl 1497: { &vop_truncate_desc, ext2fs_truncate }, /* truncate */
1498: { &vop_update_desc, ext2fs_update }, /* update */
1.1 bouyer 1499: { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
1.30 chs 1500: { &vop_getpages_desc, genfs_getpages }, /* getpages */
1501: { &vop_putpages_desc, genfs_putpages }, /* putpages */
1502: { NULL, NULL }
1.1 bouyer 1503: };
1.31 jdolecek 1504: const struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
1.1 bouyer 1505: { &ext2fs_vnodeop_p, ext2fs_vnodeop_entries };
1506:
1507: int (**ext2fs_specop_p) __P((void *));
1.31 jdolecek 1508: const struct vnodeopv_entry_desc ext2fs_specop_entries[] = {
1.1 bouyer 1509: { &vop_default_desc, vn_default_error },
1510: { &vop_lookup_desc, spec_lookup }, /* lookup */
1511: { &vop_create_desc, spec_create }, /* create */
1512: { &vop_mknod_desc, spec_mknod }, /* mknod */
1513: { &vop_open_desc, spec_open }, /* open */
1514: { &vop_close_desc, ufsspec_close }, /* close */
1.8 fvdl 1515: { &vop_access_desc, ext2fs_access }, /* access */
1516: { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
1517: { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
1.1 bouyer 1518: { &vop_read_desc, ufsspec_read }, /* read */
1519: { &vop_write_desc, ufsspec_write }, /* write */
1.8 fvdl 1520: { &vop_lease_desc, spec_lease_check }, /* lease */
1.1 bouyer 1521: { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
1.21 wrstuden 1522: { &vop_fcntl_desc, ufs_fcntl }, /* fcntl */
1.1 bouyer 1523: { &vop_poll_desc, spec_poll }, /* poll */
1.42 ! jdolecek 1524: { &vop_kqfilter_desc, spec_kqfilter }, /* kqfilter */
1.8 fvdl 1525: { &vop_revoke_desc, spec_revoke }, /* revoke */
1.1 bouyer 1526: { &vop_mmap_desc, spec_mmap }, /* mmap */
1527: { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
1528: { &vop_seek_desc, spec_seek }, /* seek */
1529: { &vop_remove_desc, spec_remove }, /* remove */
1530: { &vop_link_desc, spec_link }, /* link */
1531: { &vop_rename_desc, spec_rename }, /* rename */
1532: { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
1533: { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
1.8 fvdl 1534: { &vop_symlink_desc, spec_symlink }, /* symlink */
1535: { &vop_readdir_desc, spec_readdir }, /* readdir */
1536: { &vop_readlink_desc, spec_readlink }, /* readlink */
1537: { &vop_abortop_desc, spec_abortop }, /* abortop */
1538: { &vop_inactive_desc, ext2fs_inactive }, /* inactive */
1539: { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
1.1 bouyer 1540: { &vop_lock_desc, ufs_lock }, /* lock */
1541: { &vop_unlock_desc, ufs_unlock }, /* unlock */
1542: { &vop_bmap_desc, spec_bmap }, /* bmap */
1.8 fvdl 1543: { &vop_strategy_desc, spec_strategy }, /* strategy */
1.1 bouyer 1544: { &vop_print_desc, ufs_print }, /* print */
1.8 fvdl 1545: { &vop_islocked_desc, ufs_islocked }, /* islocked */
1546: { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
1547: { &vop_advlock_desc, spec_advlock }, /* advlock */
1548: { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
1.1 bouyer 1549: { &vop_valloc_desc, spec_valloc }, /* valloc */
1550: { &vop_vfree_desc, ext2fs_vfree }, /* vfree */
1.8 fvdl 1551: { &vop_truncate_desc, spec_truncate }, /* truncate */
1552: { &vop_update_desc, ext2fs_update }, /* update */
1.1 bouyer 1553: { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
1.35 chs 1554: { &vop_getpages_desc, spec_getpages }, /* getpages */
1555: { &vop_putpages_desc, spec_putpages }, /* putpages */
1.30 chs 1556: { NULL, NULL }
1.1 bouyer 1557: };
1.31 jdolecek 1558: const struct vnodeopv_desc ext2fs_specop_opv_desc =
1.1 bouyer 1559: { &ext2fs_specop_p, ext2fs_specop_entries };
1560:
1561: int (**ext2fs_fifoop_p) __P((void *));
1.31 jdolecek 1562: const struct vnodeopv_entry_desc ext2fs_fifoop_entries[] = {
1.1 bouyer 1563: { &vop_default_desc, vn_default_error },
1564: { &vop_lookup_desc, fifo_lookup }, /* lookup */
1565: { &vop_create_desc, fifo_create }, /* create */
1566: { &vop_mknod_desc, fifo_mknod }, /* mknod */
1567: { &vop_open_desc, fifo_open }, /* open */
1568: { &vop_close_desc, ufsfifo_close }, /* close */
1.8 fvdl 1569: { &vop_access_desc, ext2fs_access }, /* access */
1570: { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
1571: { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
1.1 bouyer 1572: { &vop_read_desc, ufsfifo_read }, /* read */
1573: { &vop_write_desc, ufsfifo_write }, /* write */
1.8 fvdl 1574: { &vop_lease_desc, fifo_lease_check }, /* lease */
1.1 bouyer 1575: { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
1.21 wrstuden 1576: { &vop_fcntl_desc, ufs_fcntl }, /* fcntl */
1.1 bouyer 1577: { &vop_poll_desc, fifo_poll }, /* poll */
1.42 ! jdolecek 1578: { &vop_kqfilter_desc, fifo_kqfilter }, /* kqfilter */
1.8 fvdl 1579: { &vop_revoke_desc, fifo_revoke }, /* revoke */
1.1 bouyer 1580: { &vop_mmap_desc, fifo_mmap }, /* mmap */
1581: { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
1582: { &vop_seek_desc, fifo_seek }, /* seek */
1583: { &vop_remove_desc, fifo_remove }, /* remove */
1584: { &vop_link_desc, fifo_link }, /* link */
1585: { &vop_rename_desc, fifo_rename }, /* rename */
1586: { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
1587: { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
1.8 fvdl 1588: { &vop_symlink_desc, fifo_symlink }, /* symlink */
1589: { &vop_readdir_desc, fifo_readdir }, /* readdir */
1590: { &vop_readlink_desc, fifo_readlink }, /* readlink */
1591: { &vop_abortop_desc, fifo_abortop }, /* abortop */
1592: { &vop_inactive_desc, ext2fs_inactive }, /* inactive */
1593: { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
1.1 bouyer 1594: { &vop_lock_desc, ufs_lock }, /* lock */
1595: { &vop_unlock_desc, ufs_unlock }, /* unlock */
1596: { &vop_bmap_desc, fifo_bmap }, /* bmap */
1.8 fvdl 1597: { &vop_strategy_desc, fifo_strategy }, /* strategy */
1.1 bouyer 1598: { &vop_print_desc, ufs_print }, /* print */
1.8 fvdl 1599: { &vop_islocked_desc, ufs_islocked }, /* islocked */
1600: { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
1601: { &vop_advlock_desc, fifo_advlock }, /* advlock */
1602: { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
1.1 bouyer 1603: { &vop_valloc_desc, fifo_valloc }, /* valloc */
1604: { &vop_vfree_desc, ext2fs_vfree }, /* vfree */
1.8 fvdl 1605: { &vop_truncate_desc, fifo_truncate }, /* truncate */
1606: { &vop_update_desc, ext2fs_update }, /* update */
1.1 bouyer 1607: { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
1.38 sommerfe 1608: { &vop_putpages_desc, fifo_putpages }, /* putpages */
1.30 chs 1609: { NULL, NULL }
1.1 bouyer 1610: };
1.31 jdolecek 1611: const struct vnodeopv_desc ext2fs_fifoop_opv_desc =
1.1 bouyer 1612: { &ext2fs_fifoop_p, ext2fs_fifoop_entries };
CVSweb <webmaster@jp.NetBSD.org>