hammer2 - more indirect block work, add advlock
[dragonfly.git] / sys / vfs / hammer2 / hammer2_vnops.c
1 /*
2  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/fcntl.h>
39 #include <sys/buf.h>
40 #include <sys/proc.h>
41 #include <sys/namei.h>
42 #include <sys/mount.h>
43 #include <sys/vnode.h>
44 #include <sys/mountctl.h>
45 #include <sys/dirent.h>
46
47 #include "hammer2.h"
48
49 #define ZFOFFSET        (-2LL)
50
51 /*
52  * Last reference to a vnode is going away but it is still cached.
53  */
54 static
55 int
56 hammer2_vop_inactive(struct vop_inactive_args *ap)
57 {
58         struct vnode *vp;
59         struct hammer2_inode *ip;
60 #if 0
61         struct hammer2_mount *hmp;
62 #endif
63
64         vp = ap->a_vp;
65         ip = VTOI(vp);
66
67         /*
68          * Degenerate case
69          */
70         if (ip == NULL) {
71                 vrecycle(vp);
72                 return (0);
73         }
74
75         return (0);
76 }
77
78 /*
79  * Reclaim a vnode so that it can be reused; after the inode is
80  * disassociated, the filesystem must manage it alone.
81  */
82 static
83 int
84 hammer2_vop_reclaim(struct vop_reclaim_args *ap)
85 {
86         struct hammer2_inode *ip;
87         struct hammer2_mount *hmp;
88         struct vnode *vp;
89
90         vp = ap->a_vp;
91         ip = VTOI(vp);
92         if (ip == NULL)
93                 return(0);
94         hmp = ip->hmp;
95
96         hammer2_inode_lock_ex(ip);
97         vp->v_data = NULL;
98         ip->vp = NULL;
99         hammer2_chain_flush(hmp, &ip->chain, NULL);
100         hammer2_inode_unlock_ex(ip);
101         hammer2_chain_drop(hmp, &ip->chain);    /* vp ref removed */
102
103         /*
104          * XXX handle background sync when ip dirty, kernel will no longer
105          * notify us regarding this inode because there is no longer a
106          * vnode attached to it.
107          */
108
109         return (0);
110 }
111
112 static
113 int
114 hammer2_vop_fsync(struct vop_fsync_args *ap)
115 {
116         struct hammer2_inode *ip;
117         struct hammer2_mount *hmp;
118         struct vnode *vp;
119
120         vp = ap->a_vp;
121         ip = VTOI(vp);
122         hmp = ip->hmp;
123
124         hammer2_inode_lock_ex(ip);
125         vfsync(vp, ap->a_waitfor, 1, NULL, NULL);
126         hammer2_chain_flush(hmp, &ip->chain, NULL);
127         hammer2_inode_unlock_ex(ip);
128         return (0);
129 }
130
131 static
132 int
133 hammer2_vop_access(struct vop_access_args *ap)
134 {
135         hammer2_inode_t *ip = VTOI(ap->a_vp);
136         uid_t uid;
137         gid_t gid;
138         int error;
139
140         uid = hammer2_to_unix_xid(&ip->ip_data.uid);
141         gid = hammer2_to_unix_xid(&ip->ip_data.gid);
142
143         error = vop_helper_access(ap, uid, gid, ip->ip_data.mode,
144                                   ip->ip_data.uflags);
145         return (error);
146 }
147
148 static
149 int
150 hammer2_vop_getattr(struct vop_getattr_args *ap)
151 {
152         hammer2_mount_t *hmp;
153         hammer2_inode_t *ip;
154         struct vnode *vp;
155         struct vattr *vap;
156
157         vp = ap->a_vp;
158         vap = ap->a_vap;
159
160         ip = VTOI(vp);
161         hmp = ip->hmp;
162
163         hammer2_inode_lock_sh(ip);
164
165         vap->va_fsid = hmp->mp->mnt_stat.f_fsid.val[0];
166         vap->va_fileid = ip->ip_data.inum;
167         vap->va_mode = ip->ip_data.mode;
168         vap->va_nlink = ip->ip_data.nlinks;
169         vap->va_uid = 0;
170         vap->va_gid = 0;
171         vap->va_rmajor = 0;
172         vap->va_rminor = 0;
173         vap->va_size = ip->ip_data.size;
174         vap->va_blocksize = HAMMER2_PBUFSIZE;
175         vap->va_flags = ip->ip_data.uflags;
176         hammer2_time_to_timespec(ip->ip_data.ctime, &vap->va_ctime);
177         hammer2_time_to_timespec(ip->ip_data.mtime, &vap->va_mtime);
178         hammer2_time_to_timespec(ip->ip_data.mtime, &vap->va_atime);
179         vap->va_gen = 1;
180         vap->va_bytes = vap->va_size;
181         vap->va_type = hammer2_get_vtype(ip);
182         vap->va_filerev = 0;
183         vap->va_uid_uuid = ip->ip_data.uid;
184         vap->va_gid_uuid = ip->ip_data.gid;
185         vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
186                           VA_FSID_UUID_VALID;
187
188         hammer2_inode_unlock_sh(ip);
189
190         return (0);
191 }
192
193 static
194 int
195 hammer2_vop_readdir(struct vop_readdir_args *ap)
196 {
197         hammer2_mount_t *hmp;
198         hammer2_inode_t *ip;
199         hammer2_inode_t *xip;
200         hammer2_chain_t *parent;
201         hammer2_chain_t *chain;
202         hammer2_key_t lkey;
203         struct uio *uio;
204         off_t *cookies;
205         off_t saveoff;
206         int cookie_index;
207         int ncookies;
208         int error;
209         int dtype;
210         int r;
211
212         ip = VTOI(ap->a_vp);
213         hmp = ip->hmp;
214         uio = ap->a_uio;
215         saveoff = uio->uio_offset;
216
217         /*
218          * Setup cookies directory entry cookies if requested
219          */
220         if (ap->a_ncookies) {
221                 ncookies = uio->uio_resid / 16 + 1;
222                 if (ncookies > 1024)
223                         ncookies = 1024;
224                 cookies = kmalloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK);
225         } else {
226                 ncookies = -1;
227                 cookies = NULL;
228         }
229         cookie_index = 0;
230
231         /*
232          * Handle artificial entries.  To ensure that only positive 64 bit
233          * quantities are returned to userland we always strip off bit 63.
234          * The hash code is designed such that codes 0x0000-0x7FFF are not
235          * used, allowing us to use these codes for articial entries.
236          *
237          * Entry 0 is used for '.' and entry 1 is used for '..'.  Do not
238          * allow '..' to cross the mount point into (e.g.) the super-root.
239          */
240         error = 0;
241         chain = (void *)(intptr_t)-1;   /* non-NULL for early goto done case */
242
243         if (saveoff == 0) {
244                 r = vop_write_dirent(&error, uio,
245                                      ip->ip_data.inum &
246                                         HAMMER2_DIRHASH_USERMSK,
247                                      DT_DIR, 1, ".");
248                 if (r)
249                         goto done;
250                 if (cookies)
251                         cookies[cookie_index] = saveoff;
252                 ++saveoff;
253                 ++cookie_index;
254                 if (cookie_index == ncookies)
255                         goto done;
256         }
257         if (saveoff == 1) {
258                 if (ip->pip == NULL || ip == hmp->iroot)
259                         xip = ip;
260                 else
261                         xip = ip->pip;
262
263                 r = vop_write_dirent(&error, uio,
264                                      xip->ip_data.inum &
265                                       HAMMER2_DIRHASH_USERMSK,
266                                      DT_DIR, 2, "..");
267                 if (r)
268                         goto done;
269                 if (cookies)
270                         cookies[cookie_index] = saveoff;
271                 ++saveoff;
272                 ++cookie_index;
273                 if (cookie_index == ncookies)
274                         goto done;
275         }
276
277         lkey = saveoff | HAMMER2_DIRHASH_VISIBLE;
278
279         parent = &ip->chain;
280         hammer2_chain_ref(hmp, parent);
281         error = hammer2_chain_lock(hmp, parent);
282         if (error) {
283                 hammer2_chain_put(hmp, parent);
284                 goto done;
285         }
286         chain = hammer2_chain_lookup(hmp, &parent, lkey, lkey, 0);
287         if (chain == NULL) {
288                 chain = hammer2_chain_lookup(hmp, &parent,
289                                              lkey, (hammer2_key_t)-1, 0);
290         }
291         while (chain) {
292                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
293                         dtype = hammer2_get_dtype(chain->u.ip);
294                         saveoff = chain->bref.key & HAMMER2_DIRHASH_USERMSK;
295                         r = vop_write_dirent(&error, uio,
296                                              chain->u.ip->ip_data.inum &
297                                               HAMMER2_DIRHASH_USERMSK,
298                                              dtype, chain->u.ip->ip_data.name_len,
299                                              chain->u.ip->ip_data.filename);
300                         if (r)
301                                 break;
302                         if (cookies)
303                                 cookies[cookie_index] = saveoff;
304                         ++cookie_index;
305                 } else {
306                         /* XXX chain error */
307                         kprintf("bad chain type readdir %d\n",
308                                 chain->bref.type);
309                 }
310
311                 /*
312                  * Keys may not be returned in order so once we have a
313                  * placemarker (chain) the scan must allow the full range
314                  * or some entries will be missed.
315                  */
316                 chain = hammer2_chain_next(hmp, &parent, chain,
317                                            0, (hammer2_key_t)-1, 0);
318                 if (chain) {
319                         saveoff = (chain->bref.key &
320                                    HAMMER2_DIRHASH_USERMSK) + 1;
321                 } else {
322                         saveoff = (hammer2_key_t)-1;
323                 }
324                 if (cookie_index == ncookies)
325                         break;
326         }
327         hammer2_chain_put(hmp, parent);
328         if (chain)
329                 hammer2_chain_put(hmp, chain);
330 done:
331         if (ap->a_eofflag)
332                 *ap->a_eofflag = (chain == NULL);
333         uio->uio_offset = saveoff & ~HAMMER2_DIRHASH_VISIBLE;
334         if (error && cookie_index == 0) {
335                 if (cookies) {
336                         kfree(cookies, M_TEMP);
337                         *ap->a_ncookies = 0;
338                         *ap->a_cookies = NULL;
339                 }
340         } else {
341                 if (cookies) {
342                         *ap->a_ncookies = cookie_index;
343                         *ap->a_cookies = cookies;
344                 }
345         }
346         return (error);
347 }
348
349 static
350 int
351 hammer2_vop_read(struct vop_read_args *ap)
352 {
353         struct vnode *vp;
354         hammer2_mount_t *hmp;
355         hammer2_inode_t *ip;
356         struct buf *bp;
357         struct uio *uio;
358         int error;
359         int seqcount;
360         int bigread;
361
362         /*
363          * Read operations supported on this vnode?
364          */
365         vp = ap->a_vp;
366         if (vp->v_type != VREG)
367                 return (EINVAL);
368
369         /*
370          * Misc
371          */
372         ip = VTOI(vp);
373         hmp = ip->hmp;
374         uio = ap->a_uio;
375         error = 0;
376
377         seqcount = ap->a_ioflag >> 16;
378         bigread = (uio->uio_resid > 100 * 1024 * 1024);
379
380         /*
381          * UIO read loop
382          */
383         while (uio->uio_resid > 0 && uio->uio_offset < ip->ip_data.size) {
384                 hammer2_key_t off_hi;
385                 int off_lo;
386                 int n;
387
388                 off_hi = uio->uio_offset & ~HAMMER2_LBUFMASK64;
389                 off_lo = (int)(uio->uio_offset & HAMMER2_LBUFMASK64);
390
391                 /* XXX bigread & signal check test */
392
393                 error = cluster_read(vp, ip->ip_data.size, off_hi,
394                                      HAMMER2_LBUFSIZE, HAMMER2_PBUFSIZE,
395                                      seqcount * BKVASIZE, &bp);
396                 if (error)
397                         break;
398                 n = HAMMER2_LBUFSIZE - off_lo;
399                 if (n > uio->uio_resid)
400                         n = uio->uio_resid;
401                 if (n > ip->ip_data.size - uio->uio_offset)
402                         n = (int)(ip->ip_data.size - uio->uio_offset);
403                 bp->b_flags |= B_AGE;
404                 uiomove((char *)bp->b_data + off_lo, n, uio);
405                 bqrelse(bp);
406         }
407         return (error);
408 }
409
410 static
411 int
412 hammer2_vop_write(struct vop_write_args *ap)
413 {
414         thread_t td;
415         struct vnode *vp;
416         hammer2_mount_t *hmp;
417         hammer2_inode_t *ip;
418         struct buf *bp;
419         struct uio *uio;
420         int error;
421         int kflags;
422         int seqcount;
423         int bigwrite;
424
425         /*
426          * Read operations supported on this vnode?
427          */
428         vp = ap->a_vp;
429         if (vp->v_type != VREG)
430                 return (EINVAL);
431
432         /*
433          * Misc
434          */
435         ip = VTOI(vp);
436         hmp = ip->hmp;
437         uio = ap->a_uio;
438         error = 0;
439         kflags = 0;
440         if (hmp->ronly)
441                 return (EROFS);
442
443         seqcount = ap->a_ioflag >> 16;
444         bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
445
446         /*
447          * Check resource limit
448          */
449         if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc &&
450             uio->uio_offset + uio->uio_resid >
451              td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
452                 lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
453                 return (EFBIG);
454         }
455
456         bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
457
458         /*
459          * UIO read loop
460          */
461         while (uio->uio_resid > 0) {
462                 hammer2_key_t nsize;
463                 hammer2_key_t off_hi;
464                 int fixsize;
465                 int off_lo;
466                 int n;
467                 int trivial;
468                 int endofblk;
469
470                 off_hi = uio->uio_offset & ~HAMMER2_LBUFMASK64;
471                 off_lo = (int)(uio->uio_offset & HAMMER2_LBUFMASK64);
472
473                 n = HAMMER2_LBUFSIZE - off_lo;
474                 if (n > uio->uio_resid) {
475                         n = uio->uio_resid;
476                         endofblk = 0;
477                 } else {
478                         endofblk = 1;
479                 }
480                 nsize = uio->uio_offset + n;
481
482                 /* XXX bigwrite & signal check test */
483
484                 /*
485                  * Don't allow the buffer build to blow out the buffer
486                  * cache.
487                  */
488                 if ((ap->a_ioflag & IO_RECURSE) == 0)
489                         bwillwrite(HAMMER2_LBUFSIZE);
490
491                 /*
492                  * Extend the size of the file as needed
493                  * XXX lock.
494                  */
495                 if (nsize > ip->ip_data.size) {
496                         if (uio->uio_offset > ip->ip_data.size)
497                                 trivial = 0;
498                         else
499                                 trivial = 1;
500                         nvextendbuf(vp, ip->ip_data.size, nsize,
501                                     HAMMER2_LBUFSIZE, HAMMER2_LBUFSIZE,
502                                     (int)(ip->ip_data.size & HAMMER2_LBUFMASK),
503                                     (int)(nsize),
504                                     trivial);
505                         kflags |= NOTE_EXTEND;
506                         fixsize = 1;
507                 } else {
508                         fixsize = 0;
509                 }
510
511                 if (uio->uio_segflg == UIO_NOCOPY) {
512                         /*
513                          * Issuing a write with the same data backing the
514                          * buffer.  Instantiate the buffer to collect the
515                          * backing vm pages, then read-in any missing bits.
516                          *
517                          * This case is used by vop_stdputpages().
518                          */
519                         bp = getblk(vp, off_hi,
520                                     HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
521                         if ((bp->b_flags & B_CACHE) == 0) {
522                                 bqrelse(bp);
523                                 error = bread(ap->a_vp,
524                                               off_hi, HAMMER2_LBUFSIZE, &bp);
525                         }
526                 } else if (off_lo == 0 && uio->uio_resid >= HAMMER2_LBUFSIZE) {
527                         /*
528                          * Even though we are entirely overwriting the buffer
529                          * we may still have to zero it out to avoid a
530                          * mmap/write visibility issue.
531                          */
532                         bp = getblk(vp, off_hi,
533                                     HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
534                         if ((bp->b_flags & B_CACHE) == 0)
535                                 vfs_bio_clrbuf(bp);
536                 } else if (off_hi >= ip->ip_data.size) {
537                         /*
538                          * If the base offset of the buffer is beyond the
539                          * file EOF, we don't have to issue a read.
540                          */
541                         bp = getblk(vp, off_hi,
542                                     HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
543                         vfs_bio_clrbuf(bp);
544                 } else {
545                         /*
546                          * Partial overwrite, read in any missing bits then
547                          * replace the portion being written.
548                          */
549                         error = bread(vp, off_hi, HAMMER2_LBUFSIZE, &bp);
550                         if (error == 0)
551                                 bheavy(bp);
552                 }
553
554                 if (error == 0) {
555                         /* release lock */
556                         error = uiomove(bp->b_data + off_lo, n, uio);
557                         /* acquire lock */
558                 }
559
560                 if (error) {
561                         brelse(bp);
562                         if (fixsize) {
563                                 nvtruncbuf(vp, ip->ip_data.size,
564                                            HAMMER2_LBUFSIZE, HAMMER2_LBUFSIZE);
565                         }
566                         break;
567                 }
568                 kflags |= NOTE_WRITE;
569                 if (ip->ip_data.size < uio->uio_offset)
570                         ip->ip_data.size = uio->uio_offset;
571                 /* XXX update ino_data.mtime */
572
573                 /*
574                  * Once we dirty a buffer any cached offset becomes invalid.
575                  */
576                 bp->b_bio2.bio_offset = NOOFFSET;
577                 bp->b_flags |= B_AGE;
578                 if (ap->a_ioflag & IO_SYNC) {
579                         bwrite(bp);
580                 } else if ((ap->a_ioflag & IO_DIRECT) && endofblk) {
581                         bawrite(bp);
582                 } else if (ap->a_ioflag & IO_ASYNC) {
583                         bawrite(bp);
584                 } else {
585                         bdwrite(bp);
586                 }
587         }
588         /* hammer2_knote(vp, kflags); */
589         return (error);
590 }
591
592 static
593 int
594 hammer2_vop_nresolve(struct vop_nresolve_args *ap)
595 {
596         hammer2_inode_t *dip;
597         hammer2_mount_t *hmp;
598         hammer2_chain_t *parent;
599         hammer2_chain_t *chain;
600         struct namecache *ncp;
601         const uint8_t *name;
602         size_t name_len;
603         hammer2_key_t lhc;
604         int error = 0;
605         struct vnode *vp;
606
607         dip = VTOI(ap->a_dvp);
608         hmp = dip->hmp;
609         ncp = ap->a_nch->ncp;
610         name = ncp->nc_name;
611         name_len = ncp->nc_nlen;
612         lhc = hammer2_dirhash(name, name_len);
613
614         /*
615          * Note: In DragonFly the kernel handles '.' and '..'.
616          */
617         parent = &dip->chain;
618         hammer2_chain_ref(hmp, parent);
619         hammer2_chain_lock(hmp, parent);
620         chain = hammer2_chain_lookup(hmp, &parent,
621                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
622                                      0);
623         while (chain) {
624                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
625                     chain->u.ip &&
626                     name_len == chain->data->ipdata.name_len &&
627                     bcmp(name, chain->data->ipdata.filename, name_len) == 0) {
628                         break;
629                 }
630                 chain = hammer2_chain_next(hmp, &parent, chain,
631                                            lhc, lhc + HAMMER2_DIRHASH_LOMASK,
632                                            0);
633         }
634         hammer2_chain_put(hmp, parent);
635
636         if (chain) {
637                 vp = hammer2_igetv(chain->u.ip, &error);
638                 if (error == 0) {
639                         vn_unlock(vp);
640                         cache_setvp(ap->a_nch, vp);
641                         vrele(vp);
642                 }
643                 hammer2_chain_put(hmp, chain);
644         } else {
645                 error = ENOENT;
646                 cache_setvp(ap->a_nch, NULL);
647         }
648         return error;
649 }
650
651 static
652 int
653 hammer2_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
654 {
655         hammer2_inode_t *dip;
656         hammer2_inode_t *ip;
657         hammer2_mount_t *hmp;
658         int error;
659
660         dip = VTOI(ap->a_dvp);
661         hmp = dip->hmp;
662
663         if ((ip = dip->pip) == NULL) {
664                 *ap->a_vpp = NULL;
665                 return ENOENT;
666         }
667         hammer2_chain_ref(hmp, &ip->chain);
668         hammer2_chain_lock(hmp, &ip->chain);
669         *ap->a_vpp = hammer2_igetv(ip, &error);
670         hammer2_chain_put(hmp, &ip->chain);
671
672         return error;
673 }
674
675 static
676 int
677 hammer2_vop_nmkdir(struct vop_nmkdir_args *ap)
678 {
679         hammer2_mount_t *hmp;
680         hammer2_inode_t *dip;
681         hammer2_inode_t *nip;
682         struct namecache *ncp;
683         const uint8_t *name;
684         size_t name_len;
685         int error;
686
687         dip = VTOI(ap->a_dvp);
688         hmp = dip->hmp;
689         if (hmp->ronly)
690                 return (EROFS);
691
692         ncp = ap->a_nch->ncp;
693         name = ncp->nc_name;
694         name_len = ncp->nc_nlen;
695
696         error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
697                                      dip, name, name_len, &nip);
698         if (error) {
699                 KKASSERT(nip == NULL);
700                 *ap->a_vpp = NULL;
701                 return error;
702         }
703         *ap->a_vpp = hammer2_igetv(nip, &error);
704         hammer2_chain_put(hmp, &nip->chain);
705
706         if (error == 0) {
707                 cache_setunresolved(ap->a_nch);
708                 cache_setvp(ap->a_nch, *ap->a_vpp);
709         }
710         return error;
711 }
712
713 /*
714  * Return the largest contiguous physical disk range for the logical
715  * request.
716  *
717  * (struct vnode *vp, off_t loffset, off_t *doffsetp, int *runp, int *runb)
718  */
719 static
720 int
721 hammer2_vop_bmap(struct vop_bmap_args *ap)
722 {
723         struct vnode *vp;
724         hammer2_mount_t *hmp;
725         hammer2_inode_t *ip;
726         hammer2_chain_t *parent;
727         hammer2_chain_t *chain;
728         hammer2_key_t loff;
729         hammer2_off_t poff;
730
731         /*
732          * Only supported on regular files
733          *
734          * Only supported for read operations (required for cluster_read).
735          * The block allocation is delayed for write operations.
736          */
737         vp = ap->a_vp;
738         if (vp->v_type != VREG)
739                 return (EOPNOTSUPP);
740         if (ap->a_cmd != BUF_CMD_READ)
741                 return (EOPNOTSUPP);
742
743         ip = VTOI(vp);
744         hmp = ip->hmp;
745
746         loff = ap->a_loffset;
747         KKASSERT((loff & HAMMER2_LBUFMASK64) == 0);
748
749         parent = &ip->chain;
750         hammer2_chain_ref(hmp, parent);
751         hammer2_chain_lock(hmp, parent);
752         chain = hammer2_chain_lookup(hmp, &parent, loff, loff, 0);
753         if (chain) {
754                 poff = loff - chain->bref.key +
755                        (chain->bref.data_off & HAMMER2_OFF_MASK);
756                 *ap->a_doffsetp = poff;
757                 hammer2_chain_put(hmp, chain);
758         } else {
759                 *ap->a_doffsetp = ZFOFFSET;     /* zero-fill hole */
760         }
761         hammer2_chain_put(hmp, parent);
762         return (0);
763 }
764
765 static
766 int
767 hammer2_vop_open(struct vop_open_args *ap)
768 {
769         return vop_stdopen(ap);
770 }
771
772 /*
773  * hammer_vop_advlock { vp, id, op, fl, flags }
774  *
775  * MPSAFE - does not require fs_token
776  */
777 static
778 int
779 hammer2_vop_advlock(struct vop_advlock_args *ap)
780 {
781         hammer2_inode_t *ip = VTOI(ap->a_vp);
782
783         return (lf_advlock(ap, &ip->advlock, ip->ip_data.size));
784 }
785
786
787 static
788 int
789 hammer2_vop_close(struct vop_close_args *ap)
790 {
791         return vop_stdclose(ap);
792 }
793
794 /*
795  * hammer_vop_ncreate { nch, dvp, vpp, cred, vap }
796  *
797  * The operating system has already ensured that the directory entry
798  * does not exist and done all appropriate namespace locking.
799  */
800 static
801 int
802 hammer2_vop_ncreate(struct vop_ncreate_args *ap)
803 {
804         hammer2_mount_t *hmp;
805         hammer2_inode_t *dip;
806         hammer2_inode_t *nip;
807         struct namecache *ncp;
808         const uint8_t *name;
809         size_t name_len;
810         int error;
811
812         dip = VTOI(ap->a_dvp);
813         hmp = dip->hmp;
814         if (hmp->ronly)
815                 return (EROFS);
816
817         ncp = ap->a_nch->ncp;
818         name = ncp->nc_name;
819         name_len = ncp->nc_nlen;
820
821         error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
822                                      dip, name, name_len, &nip);
823         if (error) {
824                 KKASSERT(nip == NULL);
825                 *ap->a_vpp = NULL;
826                 return error;
827         }
828         *ap->a_vpp = hammer2_igetv(nip, &error);
829         hammer2_chain_put(hmp, &nip->chain);
830
831         if (error == 0) {
832                 cache_setunresolved(ap->a_nch);
833                 cache_setvp(ap->a_nch, *ap->a_vpp);
834         }
835         return error;
836 }
837
838 static int hammer2_strategy_read(struct vop_strategy_args *ap);
839 static int hammer2_strategy_write(struct vop_strategy_args *ap);
840
841 static
842 int
843 hammer2_vop_strategy(struct vop_strategy_args *ap)
844 {
845         struct bio *biop;
846         struct buf *bp;
847         int error;
848
849         biop = ap->a_bio;
850         bp = biop->bio_buf;
851
852         switch(bp->b_cmd) {
853         case BUF_CMD_READ:
854                 error = hammer2_strategy_read(ap);
855                 break;
856         case BUF_CMD_WRITE:
857                 error = hammer2_strategy_write(ap);
858                 break;
859         default:
860                 bp->b_error = error = EINVAL;
861                 bp->b_flags |= B_ERROR;
862                 biodone(biop);
863                 break;
864         }
865
866         return (error);
867 }
868
869 static
870 int
871 hammer2_strategy_read(struct vop_strategy_args *ap)
872 {
873         struct buf *bp;
874         struct bio *bio;
875         struct bio *nbio;
876         hammer2_mount_t *hmp;
877         hammer2_inode_t *ip;
878         hammer2_chain_t *parent;
879         hammer2_chain_t *chain;
880         hammer2_key_t loff;
881         hammer2_off_t poff;
882
883         bio = ap->a_bio;
884         bp = bio->bio_buf;
885         ip = VTOI(ap->a_vp);
886         hmp = ip->hmp;
887         nbio = push_bio(bio);
888
889         if (nbio->bio_offset == NOOFFSET) {
890                 loff = bio->bio_offset;
891                 KKASSERT((loff & HAMMER2_LBUFMASK64) == 0);
892
893                 parent = &ip->chain;
894                 hammer2_chain_ref(hmp, parent);
895                 hammer2_chain_lock(hmp, parent);
896
897                 /*
898                  * Specifying NOLOCK avoids unnecessary bread()s of the
899                  * chain element's content.  We just need the block device
900                  * offset.
901                  */
902                 chain = hammer2_chain_lookup(hmp, &parent, loff, loff,
903                                              HAMMER2_LOOKUP_NOLOCK);
904                 if (chain) {
905                         poff = loff - chain->bref.key +
906                                (chain->bref.data_off & HAMMER2_OFF_MASK);
907                         nbio->bio_offset = poff;
908                         hammer2_chain_drop(hmp, chain);
909                 } else {
910                         nbio->bio_offset = ZFOFFSET;
911                 }
912                 hammer2_chain_put(hmp, parent);
913         }
914         if (nbio->bio_offset == ZFOFFSET) {
915                 bp->b_resid = 0;
916                 bp->b_error = 0;
917                 vfs_bio_clrbuf(bp);
918                 biodone(nbio);
919         } else {
920                 vn_strategy(hmp->devvp, nbio);
921         }
922         return (0);
923 }
924
925 static
926 int
927 hammer2_strategy_write(struct vop_strategy_args *ap)
928 {
929         struct buf *bp;
930         struct bio *bio;
931         struct bio *nbio;
932         hammer2_mount_t *hmp;
933         hammer2_inode_t *ip;
934         hammer2_chain_t *parent;
935         hammer2_chain_t *chain;
936         hammer2_key_t off_hi;
937         int off_lo;
938
939         bio = ap->a_bio;
940         bp = bio->bio_buf;
941         ip = VTOI(ap->a_vp);
942         hmp = ip->hmp;
943         nbio = push_bio(bio);
944
945         /*
946          * Our bmap doesn't support writes atm, and a vop_write should
947          * clear the physical disk offset cache for the copy-on-write
948          * operation.
949          */
950         KKASSERT(nbio->bio_offset == NOOFFSET);
951
952         off_hi = bio->bio_offset & HAMMER2_OFF_MASK_HI;
953         off_lo = bio->bio_offset & HAMMER2_OFF_MASK_LO;
954         KKASSERT((bio->bio_offset & HAMMER2_LBUFMASK64) == 0);
955
956         parent = &ip->chain;
957         hammer2_chain_ref(hmp, parent);
958         hammer2_chain_lock(hmp, parent);
959         chain = hammer2_chain_lookup(hmp, &parent, off_hi, off_hi, 0);
960         if (chain) {
961                 hammer2_chain_modify(hmp, chain);
962                 bcopy(bp->b_data, chain->data->buf + off_lo, bp->b_bcount);
963         } else {
964                 chain = hammer2_chain_create(hmp, parent,
965                                              off_hi, HAMMER2_PBUFRADIX,
966                                              HAMMER2_BREF_TYPE_DATA,
967                                              HAMMER2_PBUFSIZE);
968                 bcopy(bp->b_data, chain->data->buf + off_lo, bp->b_bcount);
969         }
970         if (off_lo + bp->b_bcount == HAMMER2_PBUFSIZE)
971                 atomic_set_int(&chain->flags, HAMMER2_CHAIN_IOFLUSH);
972         hammer2_chain_put(hmp, chain);
973         hammer2_chain_put(hmp, parent);
974
975         bp->b_resid = 0;
976         bp->b_error = 0;
977         biodone(nbio);
978
979         return (0);
980 }
981
982 static
983 int 
984 hammer2_vop_mountctl(struct vop_mountctl_args *ap)
985 {
986         struct mount *mp;
987         struct hammer2_mount *hmp;
988         int rc;
989
990         switch (ap->a_op) {
991         case (MOUNTCTL_SET_EXPORT):
992                 mp = ap->a_head.a_ops->head.vv_mount;
993                 hmp = MPTOH2(mp);
994
995                 if (ap->a_ctllen != sizeof(struct export_args))
996                         rc = (EINVAL);
997                 else
998                         rc = vfs_export(mp, &hmp->export,
999                                         (const struct export_args *)ap->a_ctl);
1000                 break;
1001         default:
1002                 rc = vop_stdmountctl(ap);
1003                 break;
1004         }
1005         return (rc);
1006 }
1007
1008 struct vop_ops hammer2_vnode_vops = {
1009         .vop_default    = vop_defaultop,
1010         .vop_fsync      = hammer2_vop_fsync,
1011         .vop_getpages   = vop_stdgetpages,
1012         .vop_putpages   = vop_stdputpages,
1013         .vop_access     = hammer2_vop_access,
1014         .vop_advlock    = hammer2_vop_advlock,
1015         .vop_close      = hammer2_vop_close,
1016         .vop_ncreate    = hammer2_vop_ncreate,
1017         .vop_getattr    = hammer2_vop_getattr,
1018         .vop_readdir    = hammer2_vop_readdir,
1019         .vop_getpages   = vop_stdgetpages,
1020         .vop_putpages   = vop_stdputpages,
1021         .vop_read       = hammer2_vop_read,
1022         .vop_write      = hammer2_vop_write,
1023         .vop_open       = hammer2_vop_open,
1024         .vop_inactive   = hammer2_vop_inactive,
1025         .vop_reclaim    = hammer2_vop_reclaim,
1026         .vop_nresolve   = hammer2_vop_nresolve,
1027         .vop_nlookupdotdot = hammer2_vop_nlookupdotdot,
1028         .vop_nmkdir     = hammer2_vop_nmkdir,
1029         .vop_mountctl   = hammer2_vop_mountctl,
1030         .vop_bmap       = hammer2_vop_bmap,
1031         .vop_strategy   = hammer2_vop_strategy,
1032 };
1033
1034 struct vop_ops hammer2_spec_vops = {
1035
1036 };
1037
1038 struct vop_ops hammer2_fifo_vops = {
1039
1040 };