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