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