2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.2 2007/11/07 00:43:24 dillon Exp $
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/fcntl.h>
41 #include <sys/namecache.h>
42 #include <sys/vnode.h>
43 #include <sys/lockf.h>
44 #include <sys/event.h>
51 /*static int hammer_vop_vnoperate(struct vop_generic_args *);*/
52 static int hammer_vop_fsync(struct vop_fsync_args *);
53 static int hammer_vop_read(struct vop_read_args *);
54 static int hammer_vop_write(struct vop_write_args *);
55 static int hammer_vop_access(struct vop_access_args *);
56 static int hammer_vop_advlock(struct vop_advlock_args *);
57 static int hammer_vop_close(struct vop_close_args *);
58 static int hammer_vop_ncreate(struct vop_ncreate_args *);
59 static int hammer_vop_getattr(struct vop_getattr_args *);
60 static int hammer_vop_nresolve(struct vop_nresolve_args *);
61 static int hammer_vop_nlookupdotdot(struct vop_nlookupdotdot_args *);
62 static int hammer_vop_nlink(struct vop_nlink_args *);
63 static int hammer_vop_nmkdir(struct vop_nmkdir_args *);
64 static int hammer_vop_nmknod(struct vop_nmknod_args *);
65 static int hammer_vop_open(struct vop_open_args *);
66 static int hammer_vop_pathconf(struct vop_pathconf_args *);
67 static int hammer_vop_print(struct vop_print_args *);
68 static int hammer_vop_readdir(struct vop_readdir_args *);
69 static int hammer_vop_readlink(struct vop_readlink_args *);
70 static int hammer_vop_nremove(struct vop_nremove_args *);
71 static int hammer_vop_nrename(struct vop_nrename_args *);
72 static int hammer_vop_nrmdir(struct vop_nrmdir_args *);
73 static int hammer_vop_setattr(struct vop_setattr_args *);
74 static int hammer_vop_strategy(struct vop_strategy_args *);
75 static int hammer_vop_nsymlink(struct vop_nsymlink_args *);
76 static int hammer_vop_nwhiteout(struct vop_nwhiteout_args *);
78 struct vop_ops hammer_vnode_vops = {
79 .vop_default = vop_defaultop,
80 .vop_fsync = hammer_vop_fsync,
81 .vop_read = hammer_vop_read,
82 .vop_write = hammer_vop_write,
83 .vop_access = hammer_vop_access,
84 .vop_advlock = hammer_vop_advlock,
85 .vop_close = hammer_vop_close,
86 .vop_ncreate = hammer_vop_ncreate,
87 .vop_getattr = hammer_vop_getattr,
88 .vop_inactive = hammer_vop_inactive,
89 .vop_reclaim = hammer_vop_reclaim,
90 .vop_nresolve = hammer_vop_nresolve,
91 .vop_nlookupdotdot = hammer_vop_nlookupdotdot,
92 .vop_nlink = hammer_vop_nlink,
93 .vop_nmkdir = hammer_vop_nmkdir,
94 .vop_nmknod = hammer_vop_nmknod,
95 .vop_open = hammer_vop_open,
96 .vop_pathconf = hammer_vop_pathconf,
97 .vop_print = hammer_vop_print,
98 .vop_readdir = hammer_vop_readdir,
99 .vop_readlink = hammer_vop_readlink,
100 .vop_nremove = hammer_vop_nremove,
101 .vop_nrename = hammer_vop_nrename,
102 .vop_nrmdir = hammer_vop_nrmdir,
103 .vop_setattr = hammer_vop_setattr,
104 .vop_strategy = hammer_vop_strategy,
105 .vop_nsymlink = hammer_vop_nsymlink,
106 .vop_nwhiteout = hammer_vop_nwhiteout
112 hammer_vop_vnoperate(struct vop_generic_args *)
114 return (VOCALL(&hammer_vnode_vops, ap));
119 * hammer_vop_fsync { vp, waitfor }
123 hammer_vop_fsync(struct vop_fsync_args *ap)
129 * hammer_vop_read { vp, uio, ioflag, cred }
133 hammer_vop_read(struct vop_read_args *ap)
135 struct hammer_transaction trans;
136 struct hammer_inode *ip;
143 if (ap->a_vp->v_type != VREG)
148 hammer_start_transaction(ip->hmp, &trans);
151 * Access the data in HAMMER_BUFSIZE blocks via the buffer cache.
154 while (uio->uio_resid > 0 && uio->uio_offset < ip->ino_rec.ino_size) {
155 offset = uio->uio_offset & HAMMER_BUFMASK;
156 error = bread(ap->a_vp, uio->uio_offset - offset,
157 HAMMER_BUFSIZE, &bp);
162 n = HAMMER_BUFSIZE - offset;
163 if (n > uio->uio_resid)
165 if (n > ip->ino_rec.ino_size - uio->uio_offset)
166 n = (int)(ip->ino_rec.ino_size - uio->uio_offset);
167 error = uiomove((char *)bp->b_data + offset, n, uio);
172 ip->ino_rec.ino_atime = trans.tid;
173 hammer_modify_inode(&trans, ip, HAMMER_INODE_ITIMES);
176 hammer_commit_transaction(&trans);
181 * hammer_vop_write { vp, uio, ioflag, cred }
185 hammer_vop_write(struct vop_write_args *ap)
187 struct hammer_transaction trans;
188 struct hammer_inode *ip;
195 if (ap->a_vp->v_type != VREG)
201 * Create a transaction to cover the operations we perform.
203 hammer_start_transaction(ip->hmp, &trans);
209 if (ap->a_ioflag & IO_APPEND)
210 uio->uio_offset = ip->ino_rec.ino_size;
213 * Check for illegal write offsets. Valid range is 0...2^63-1
215 if (uio->uio_offset < 0 || uio->uio_offset + uio->uio_resid <= 0)
219 * Access the data in HAMMER_BUFSIZE blocks via the buffer cache.
221 while (uio->uio_resid > 0) {
222 offset = uio->uio_offset & HAMMER_BUFMASK;
223 if (offset == 0 && uio->uio_resid >= HAMMER_BUFSIZE) {
224 bp = getblk(ap->a_vp, uio->uio_offset, HAMMER_BUFSIZE,
226 } else if (offset == 0 && uio->uio_offset >= ip->ino_rec.ino_size) {
227 bp = getblk(ap->a_vp, uio->uio_offset, HAMMER_BUFSIZE,
231 error = bread(ap->a_vp, uio->uio_offset - offset,
232 HAMMER_BUFSIZE, &bp);
238 n = HAMMER_BUFSIZE - offset;
239 if (n > uio->uio_resid)
241 error = uiomove((char *)bp->b_data + offset, n, uio);
246 if (ip->ino_rec.ino_size < uio->uio_offset) {
247 ip->ino_rec.ino_size = uio->uio_offset;
248 ip->ino_rec.ino_mtime = trans.tid;
249 hammer_modify_inode(&trans, ip,
250 HAMMER_INODE_RDIRTY | HAMMER_INODE_ITIMES);
252 if (ap->a_ioflag & IO_SYNC) {
254 } else if (ap->a_ioflag & IO_DIRECT) {
255 /* XXX B_CLUSTEROK SUPPORT */
258 /* XXX B_CLUSTEROK SUPPORT */
263 hammer_abort_transaction(&trans);
265 hammer_commit_transaction(&trans);
270 * hammer_vop_access { vp, mode, cred }
274 hammer_vop_access(struct vop_access_args *ap)
276 struct hammer_inode *ip = VTOI(ap->a_vp);
281 uid = hammer_to_unix_xid(&ip->ino_data.uid);
282 gid = hammer_to_unix_xid(&ip->ino_data.gid);
284 error = vop_helper_access(ap, uid, gid, ip->ino_data.mode,
285 ip->ino_data.uflags);
290 * hammer_vop_advlock { vp, id, op, fl, flags }
294 hammer_vop_advlock(struct vop_advlock_args *ap)
296 struct hammer_inode *ip = VTOI(ap->a_vp);
298 return (lf_advlock(ap, &ip->advlock, ip->ino_rec.ino_size));
302 * hammer_vop_close { vp, fflag }
306 hammer_vop_close(struct vop_close_args *ap)
312 * hammer_vop_ncreate { nch, dvp, vpp, cred, vap }
314 * The operating system has already ensured that the directory entry
315 * does not exist and done all appropriate namespace locking.
319 hammer_vop_ncreate(struct vop_ncreate_args *ap)
321 struct hammer_transaction trans;
322 struct hammer_inode *dip;
323 struct hammer_inode *nip;
324 struct nchandle *nch;
328 dip = VTOI(ap->a_dvp);
331 * Create a transaction to cover the operations we perform.
333 hammer_start_transaction(dip->hmp, &trans);
336 * Create a new filesystem object of the requested type. The
337 * returned inode will be locked. We cannot hold the new
338 * inode locked while doing other manipulations.
340 error = hammer_alloc_inode(&trans, ap->a_vap, ap->a_cred, &nip);
342 hammer_abort_transaction(&trans);
346 hammer_lock_to_ref(&nip->lock);
349 * Add the new filesystem object to the directory. This will also
350 * bump the inode's link count.
352 error = hammer_add_directory(&trans, dip, nch->ncp, nip);
358 hammer_put_inode_ref(nip);
359 hammer_abort_transaction(&trans);
362 hammer_commit_transaction(&trans);
363 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
364 hammer_put_inode_ref(nip);
370 * hammer_vop_getattr { vp, vap }
374 hammer_vop_getattr(struct vop_getattr_args *ap)
376 struct hammer_inode *ip = VTOI(ap->a_vp);
377 struct vattr *vap = ap->a_vap;
380 if (cache_check_fsmid_vp(ap->a_vp, &ip->fsmid) &&
381 (vp->v_mount->mnt_flag & MNT_RDONLY) == 0 &&
386 hammer_itimes(ap->a_vp);
389 vap->va_fsid = ip->hmp->fsid_udev;
390 vap->va_fileid = ip->ino_rec.base.base.obj_id;
391 vap->va_mode = ip->ino_data.mode;
392 vap->va_nlink = ip->ino_rec.ino_nlinks;
393 vap->va_uid = hammer_to_unix_xid(&ip->ino_data.uid);
394 vap->va_gid = hammer_to_unix_xid(&ip->ino_data.gid);
397 vap->va_size = ip->ino_rec.ino_size;
398 hammer_to_timespec(ip->ino_rec.ino_atime, &vap->va_atime);
399 hammer_to_timespec(ip->ino_rec.ino_mtime, &vap->va_mtime);
400 hammer_to_timespec(ip->ino_data.ctime, &vap->va_ctime);
401 vap->va_flags = ip->ino_data.uflags;
402 vap->va_gen = 1; /* hammer inums are unique for all time */
403 vap->va_blocksize = 32768; /* XXX - extract from root volume */
404 vap->va_bytes = ip->ino_rec.ino_size;
405 vap->va_type = hammer_get_vnode_type(ip->ino_rec.base.base.obj_type);
406 vap->va_filerev = 0; /* XXX */
407 /* mtime uniquely identifies any adjustments made to the file */
408 vap->va_fsmid = ip->ino_rec.ino_mtime;
409 vap->va_uid_uuid = ip->ino_data.uid;
410 vap->va_gid_uuid = ip->ino_data.gid;
411 vap->va_fsid_uuid = ip->hmp->fsid;
412 vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
418 * hammer_vop_nresolve { nch, dvp, cred }
420 * Locate the requested directory entry.
424 hammer_vop_nresolve(struct vop_nresolve_args *ap)
426 struct hammer_base_elm key;
427 struct namecache *ncp;
428 struct hammer_inode *dip;
429 struct hammer_btree_info info;
433 const int flags = HAMMER_BTREE_GET_RECORD | HAMMER_BTREE_GET_DATA;
435 dip = VTOI(ap->a_dvp);
436 ncp = ap->a_nch->ncp;
437 namekey = hammer_directory_namekey(ncp->nc_name, ncp->nc_nlen);
439 hammer_btree_info_init(&info, dip->hmp->rootcl);
440 key.obj_id = dip->obj_id;
442 key.create_tid = dip->obj_asof;
444 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
448 * Issue a lookup on the namekey. The entry should not be found
449 * since the low bits of the key are 0. This positions our cursor
450 * properly for the iteration.
452 error = hammer_btree_lookup(&info, &key, 0);
453 if (error != ENOENT) {
460 * Iterate through the keys as long as the upper 32 bits are
463 while ((error = hammer_btree_iterate(&info.cursor, &key)) == 0) {
464 if ((error = hammer_btree_extract(&info, flags)) != 0)
466 if ((namekey ^ info.rec->base.base.key) &
467 (int64_t)0xFFFFFFFF00000000ULL) {
471 if (ncp->nc_nlen == info.rec->base.data_len &&
472 bcmp(ncp->nc_name, (void *)info.data, ncp->nc_nlen) == 0) {
477 error = hammer_vfs_vget(dip->hmp->mp, info.rec->entry.obj_id, &vp);
480 cache_setvp(ap->a_nch, vp);
483 } else if (error == ENOENT) {
484 cache_setvp(ap->a_nch, NULL);
487 hammer_btree_info_done(&info);
492 * hammer_vop_nlookupdotdot { dvp, vpp, cred }
494 * Locate the parent directory of a directory vnode.
496 * dvp is referenced but not locked. *vpp must be returned referenced and
497 * locked. A parent_obj_id of 0 does not necessarily indicate that we are
498 * at the root, instead it could indicate that the directory we were in was
503 hammer_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
505 struct hammer_inode *dip;
506 u_int64_t parent_obj_id;
508 dip = VTOI(ap->a_dvp);
509 if ((parent_obj_id = dip->ino_data.parent_obj_id) == 0) {
513 return(hammer_vfs_vget(dip->hmp->mp, parent_obj_id, ap->a_vpp));
517 * hammer_vop_nlink { nch, dvp, vp, cred }
521 hammer_vop_nlink(struct vop_nlink_args *ap)
523 struct hammer_transaction trans;
524 struct hammer_inode *dip;
525 struct hammer_inode *ip;
526 struct nchandle *nch;
530 dip = VTOI(ap->a_dvp);
534 * Create a transaction to cover the operations we perform.
536 hammer_start_transaction(dip->hmp, &trans);
539 * Add the filesystem object to the directory. Note that neither
540 * dip nor ip are referenced or locked, but their vnodes are
541 * referenced. This function will bump the inode's link count.
543 error = hammer_add_directory(&trans, dip, nch->ncp, ip);
549 hammer_abort_transaction(&trans);
551 hammer_commit_transaction(&trans);
557 * hammer_vop_nmkdir { nch, dvp, vpp, cred, vap }
559 * The operating system has already ensured that the directory entry
560 * does not exist and done all appropriate namespace locking.
564 hammer_vop_nmkdir(struct vop_nmkdir_args *ap)
566 struct hammer_transaction trans;
567 struct hammer_inode *dip;
568 struct hammer_inode *nip;
569 struct nchandle *nch;
573 dip = VTOI(ap->a_dvp);
576 * Create a transaction to cover the operations we perform.
578 hammer_start_transaction(dip->hmp, &trans);
581 * Create a new filesystem object of the requested type. The
582 * returned inode will be locked. We cannot hold the new
583 * inode locked while doing other manipulations.
585 error = hammer_alloc_inode(&trans, ap->a_vap, ap->a_cred, &nip);
587 hammer_abort_transaction(&trans);
591 hammer_lock_to_ref(&nip->lock);
594 * Add the new filesystem object to the directory. This will also
595 * bump the inode's link count.
597 error = hammer_add_directory(&trans, dip, nch->ncp, nip);
603 hammer_put_inode_ref(nip);
604 hammer_abort_transaction(&trans);
607 hammer_commit_transaction(&trans);
608 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
609 hammer_put_inode_ref(nip);
615 * hammer_vop_nmknod { nch, dvp, vpp, cred, vap }
617 * The operating system has already ensured that the directory entry
618 * does not exist and done all appropriate namespace locking.
622 hammer_vop_nmknod(struct vop_nmknod_args *ap)
624 struct hammer_transaction trans;
625 struct hammer_inode *dip;
626 struct hammer_inode *nip;
627 struct nchandle *nch;
631 dip = VTOI(ap->a_dvp);
634 * Create a transaction to cover the operations we perform.
636 hammer_start_transaction(dip->hmp, &trans);
639 * Create a new filesystem object of the requested type. The
640 * returned inode will be locked. We cannot hold the new
641 * inode locked while doing other manipulations.
643 error = hammer_alloc_inode(&trans, ap->a_vap, ap->a_cred, &nip);
645 hammer_abort_transaction(&trans);
649 hammer_lock_to_ref(&nip->lock);
652 * Add the new filesystem object to the directory. This will also
653 * bump the inode's link count.
655 error = hammer_add_directory(&trans, dip, nch->ncp, nip);
661 hammer_put_inode_ref(nip);
662 hammer_abort_transaction(&trans);
665 hammer_commit_transaction(&trans);
666 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
667 hammer_put_inode_ref(nip);
673 * hammer_vop_open { vp, mode, cred, fp }
677 hammer_vop_open(struct vop_open_args *ap)
683 * hammer_vop_pathconf { vp, name, retval }
687 hammer_vop_pathconf(struct vop_pathconf_args *ap)
693 * hammer_vop_print { vp }
697 hammer_vop_print(struct vop_print_args *ap)
703 * hammer_vop_readdir { vp, uio, cred, *eofflag }
707 hammer_vop_readdir(struct vop_readdir_args *ap)
713 * hammer_vop_readlink { vp, uio, cred }
717 hammer_vop_readlink(struct vop_readlink_args *ap)
723 * hammer_vop_nremove { nch, dvp, cred }
727 hammer_vop_nremove(struct vop_nremove_args *ap)
733 * hammer_vop_nrename { fnch, tnch, fdvp, tdvp, cred }
737 hammer_vop_nrename(struct vop_nrename_args *ap)
743 * hammer_vop_nrmdir { nch, dvp, cred }
747 hammer_vop_nrmdir(struct vop_nrmdir_args *ap)
753 * hammer_vop_setattr { vp, vap, cred }
757 hammer_vop_setattr(struct vop_setattr_args *ap)
763 * hammer_vop_nsymlink { nch, dvp, vpp, cred, vap, target }
767 hammer_vop_nsymlink(struct vop_nsymlink_args *ap)
773 * hammer_vop_nwhiteout { nch, dvp, cred, flags }
777 hammer_vop_nwhiteout(struct vop_nwhiteout_args *ap)
783 * hammer_vop_strategy { vp, bio }
787 hammer_vop_strategy(struct vop_strategy_args *ap)
793 struct hammer_data_record *data;
794 struct hammer_base_elm_t key;
795 hammer_btree_info info;
796 const int flags = HAMMER_BTREE_GET_RECORD | HAMMER_BTREE_GET_DATA;
801 hammer_btree_info_init(&info, ip->hmp->rootcl);
802 key.obj_id = ip->obj_id;
803 key.create_tid = ip->obj_asof;
805 key.rec_type = HAMMER_RECTYPE_DATA;
807 key.key = uio->uio_offset;
810 * Iterate through matching records. Note that for data records
811 * the base offset is the key - data_len, NOT the key. This way
812 * we don't have to special case a ranged search.
814 error = hammer_btree_lookup(&info, &key, 0);
815 if (error && error != ENOENT)
817 while (uio->uio_resid > 0 && uio->uio_offset < ip->ino_rec.ino_size) {
818 if ((error = hammer_btree_iterate(&info.cursor, &key)) != 0)
821 * XXX - possible to optimize the extract
823 if ((error = hammer_btree_extract(&info, flags)) != 0)
825 data = &info.rec->data;
826 base_offset = data->base.key - data->base.data_len;
827 if (uio->uio_offset < base_offset) {
828 if (base_offset - uio->uio_offset > HAMMER_BUFSIZE)
831 n = (int)(base_offset - uio->uio_offset);
832 error = uiomove(ip->hmp->zbuf, n, uio);
834 o = (int)uio->uio_offset - base_offset;
835 if (o < data->base.data_len) {
836 n = data->base.data_len - o;
837 if (n > uio->uio_resid)
839 error = uiomove((char *)info.data + o, n, uio);
847 * Issue a lookup on the namekey. The entry should not be found
848 * since the low bits of the key are 0. This positions our cursor
849 * properly for the iteration.
851 if (error != ENOENT) {
858 * Iterate through the keys as long as the upper 32 bits are
861 while ((error = hammer_btree_iterate(&info, &key, flags)) == 0) {
862 if ((namekey ^ info.rec->base.base.key) &
863 (int64_t)0xFFFFFFFF00000000ULL) {
867 if (ncp->nc_nlen == info.rec->base.data_len &&
868 bcmp(ncp->nc_name, (void *)info->data, ncp->nc_nlen) == 0) {
873 error = hammer_vfs_vget(dip->hmp->mp, info->rec->entry.obj_id, &vp);
876 cache_setvp(nch, vp);
879 } else if (error == ENOENT) {
880 cache_setvp(nch, NULL);
883 hammer_btree_info_done(&info);