ed2076f5c1c20237870d63e26d438b700dd4cc5c
[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 early done means not eof */
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, (hammer2_key_t)-1, 0);
287         while (chain) {
288                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
289                         dtype = hammer2_get_dtype(chain->u.ip);
290                         saveoff = chain->bref.key & HAMMER2_DIRHASH_USERMSK;
291                         r = vop_write_dirent(&error, uio,
292                                              chain->u.ip->ip_data.inum &
293                                               HAMMER2_DIRHASH_USERMSK,
294                                              dtype, chain->u.ip->ip_data.name_len,
295                                              chain->u.ip->ip_data.filename);
296                         if (r)
297                                 break;
298                         if (cookies)
299                                 cookies[cookie_index] = saveoff;
300                         ++cookie_index;
301                 } else {
302                         /* XXX chain error */
303                         kprintf("bad chain type readdir %d\n",
304                                 chain->bref.type);
305                 }
306
307                 /*
308                  * Keys may not be returned in order so once we have a
309                  * placemarker (chain) the scan must allow the full range
310                  * or some entries will be missed.
311                  */
312                 chain = hammer2_chain_next(hmp, &parent, chain,
313                                            0, (hammer2_key_t)-1, 0);
314                 if (chain) {
315                         saveoff = (chain->bref.key &
316                                    HAMMER2_DIRHASH_USERMSK) + 1;
317                 } else {
318                         saveoff = (hammer2_key_t)-1;
319                 }
320                 if (cookie_index == ncookies)
321                         break;
322         }
323         hammer2_chain_put(hmp, parent);
324         if (chain)
325                 hammer2_chain_put(hmp, chain);
326 done:
327         if (ap->a_eofflag)
328                 *ap->a_eofflag = (chain == NULL);
329         uio->uio_offset = saveoff;
330         if (error && cookie_index == 0) {
331                 if (cookies) {
332                         kfree(cookies, M_TEMP);
333                         *ap->a_ncookies = 0;
334                         *ap->a_cookies = NULL;
335                 }
336         } else {
337                 if (cookies) {
338                         *ap->a_ncookies = cookie_index;
339                         *ap->a_cookies = cookies;
340                 }
341         }
342         return (error);
343 }
344
345 static
346 int
347 hammer2_vop_read(struct vop_read_args *ap)
348 {
349         struct vnode *vp;
350         hammer2_mount_t *hmp;
351         hammer2_inode_t *ip;
352         struct buf *bp;
353         struct uio *uio;
354         int error;
355         int seqcount;
356         int bigread;
357
358         /*
359          * Read operations supported on this vnode?
360          */
361         vp = ap->a_vp;
362         if (vp->v_type != VREG)
363                 return (EINVAL);
364
365         /*
366          * Misc
367          */
368         ip = VTOI(vp);
369         hmp = ip->hmp;
370         uio = ap->a_uio;
371         error = 0;
372
373         seqcount = ap->a_ioflag >> 16;
374         bigread = (uio->uio_resid > 100 * 1024 * 1024);
375
376         /*
377          * UIO read loop
378          */
379         while (uio->uio_resid > 0 && uio->uio_offset < ip->ip_data.size) {
380                 hammer2_key_t off_hi;
381                 int off_lo;
382                 int n;
383
384                 off_hi = uio->uio_offset & ~HAMMER2_LBUFMASK64;
385                 off_lo = (int)(uio->uio_offset & HAMMER2_LBUFMASK64);
386
387                 /* XXX bigread & signal check test */
388
389                 error = cluster_read(vp, ip->ip_data.size, off_hi,
390                                      HAMMER2_LBUFSIZE, HAMMER2_PBUFSIZE,
391                                      seqcount * BKVASIZE, &bp);
392                 if (error)
393                         break;
394                 n = HAMMER2_LBUFSIZE - off_lo;
395                 if (n > uio->uio_resid)
396                         n = uio->uio_resid;
397                 if (n > ip->ip_data.size - uio->uio_offset)
398                         n = (int)(ip->ip_data.size - uio->uio_offset);
399                 bp->b_flags |= B_AGE;
400                 uiomove((char *)bp->b_data + off_lo, n, uio);
401                 bqrelse(bp);
402         }
403         return (error);
404 }
405
406 static
407 int
408 hammer2_vop_write(struct vop_write_args *ap)
409 {
410         thread_t td;
411         struct vnode *vp;
412         hammer2_mount_t *hmp;
413         hammer2_inode_t *ip;
414         struct buf *bp;
415         struct uio *uio;
416         int error;
417         int kflags;
418         int seqcount;
419         int bigwrite;
420
421         /*
422          * Read operations supported on this vnode?
423          */
424         vp = ap->a_vp;
425         if (vp->v_type != VREG)
426                 return (EINVAL);
427
428         /*
429          * Misc
430          */
431         ip = VTOI(vp);
432         hmp = ip->hmp;
433         uio = ap->a_uio;
434         error = 0;
435         kflags = 0;
436         if (hmp->ronly)
437                 return (EROFS);
438
439         seqcount = ap->a_ioflag >> 16;
440         bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
441
442         /*
443          * Check resource limit
444          */
445         if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc &&
446             uio->uio_offset + uio->uio_resid >
447              td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
448                 lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
449                 return (EFBIG);
450         }
451
452         bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
453
454         /*
455          * UIO read loop
456          */
457         while (uio->uio_resid > 0) {
458                 hammer2_key_t nsize;
459                 hammer2_key_t off_hi;
460                 int fixsize;
461                 int off_lo;
462                 int n;
463                 int trivial;
464                 int endofblk;
465
466                 off_hi = uio->uio_offset & ~HAMMER2_LBUFMASK64;
467                 off_lo = (int)(uio->uio_offset & HAMMER2_LBUFMASK64);
468
469                 n = HAMMER2_LBUFSIZE - off_lo;
470                 if (n > uio->uio_resid) {
471                         n = uio->uio_resid;
472                         endofblk = 0;
473                 } else {
474                         endofblk = 1;
475                 }
476                 nsize = uio->uio_offset + n;
477
478                 /* XXX bigwrite & signal check test */
479
480                 /*
481                  * Don't allow the buffer build to blow out the buffer
482                  * cache.
483                  */
484                 if ((ap->a_ioflag & IO_RECURSE) == 0)
485                         bwillwrite(HAMMER2_LBUFSIZE);
486
487                 /*
488                  * Extend the size of the file as needed
489                  * XXX lock.
490                  */
491                 if (nsize > ip->ip_data.size) {
492                         if (uio->uio_offset > ip->ip_data.size)
493                                 trivial = 0;
494                         else
495                                 trivial = 1;
496                         nvextendbuf(vp, ip->ip_data.size, nsize,
497                                     HAMMER2_LBUFSIZE, HAMMER2_LBUFSIZE,
498                                     (int)(ip->ip_data.size & HAMMER2_LBUFMASK),
499                                     (int)(nsize),
500                                     trivial);
501                         kflags |= NOTE_EXTEND;
502                         fixsize = 1;
503                 } else {
504                         fixsize = 0;
505                 }
506
507                 if (uio->uio_segflg == UIO_NOCOPY) {
508                         /*
509                          * Issuing a write with the same data backing the
510                          * buffer.  Instantiate the buffer to collect the
511                          * backing vm pages, then read-in any missing bits.
512                          *
513                          * This case is used by vop_stdputpages().
514                          */
515                         bp = getblk(vp, off_hi,
516                                     HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
517                         if ((bp->b_flags & B_CACHE) == 0) {
518                                 bqrelse(bp);
519                                 error = bread(ap->a_vp,
520                                               off_hi, HAMMER2_LBUFSIZE, &bp);
521                         }
522                 } else if (off_lo == 0 && uio->uio_resid >= HAMMER2_LBUFSIZE) {
523                         /*
524                          * Even though we are entirely overwriting the buffer
525                          * we may still have to zero it out to avoid a
526                          * mmap/write visibility issue.
527                          */
528                         bp = getblk(vp, off_hi,
529                                     HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
530                         if ((bp->b_flags & B_CACHE) == 0)
531                                 vfs_bio_clrbuf(bp);
532                 } else if (off_hi >= ip->ip_data.size) {
533                         /*
534                          * If the base offset of the buffer is beyond the
535                          * file EOF, we don't have to issue a read.
536                          */
537                         bp = getblk(vp, off_hi,
538                                     HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
539                         vfs_bio_clrbuf(bp);
540                 } else {
541                         /*
542                          * Partial overwrite, read in any missing bits then
543                          * replace the portion being written.
544                          */
545                         error = bread(vp, off_hi, HAMMER2_LBUFSIZE, &bp);
546                         if (error == 0)
547                                 bheavy(bp);
548                 }
549
550                 if (error == 0) {
551                         /* release lock */
552                         error = uiomove(bp->b_data + off_lo, n, uio);
553                         /* acquire lock */
554                 }
555
556                 if (error) {
557                         brelse(bp);
558                         if (fixsize) {
559                                 nvtruncbuf(vp, ip->ip_data.size,
560                                            HAMMER2_LBUFSIZE, HAMMER2_LBUFSIZE);
561                         }
562                         break;
563                 }
564                 kflags |= NOTE_WRITE;
565                 if (ip->ip_data.size < uio->uio_offset)
566                         ip->ip_data.size = uio->uio_offset;
567                 /* XXX update ino_data.mtime */
568
569                 /*
570                  * Once we dirty a buffer any cached offset becomes invalid.
571                  */
572                 bp->b_bio2.bio_offset = NOOFFSET;
573                 bp->b_flags |= B_AGE;
574                 if (ap->a_ioflag & IO_SYNC) {
575                         bwrite(bp);
576                 } else if ((ap->a_ioflag & IO_DIRECT) && endofblk) {
577                         bawrite(bp);
578                 } else if (ap->a_ioflag & IO_ASYNC) {
579                         bawrite(bp);
580                 } else {
581                         bdwrite(bp);
582                 }
583         }
584         /* hammer2_knote(vp, kflags); */
585         return (error);
586 }
587
588 static
589 int
590 hammer2_vop_nresolve(struct vop_nresolve_args *ap)
591 {
592         hammer2_inode_t *dip;
593         hammer2_mount_t *hmp;
594         hammer2_chain_t *parent;
595         hammer2_chain_t *chain;
596         struct namecache *ncp;
597         const uint8_t *name;
598         size_t name_len;
599         hammer2_key_t lhc;
600         int error = 0;
601         struct vnode *vp;
602
603         dip = VTOI(ap->a_dvp);
604         hmp = dip->hmp;
605         ncp = ap->a_nch->ncp;
606         name = ncp->nc_name;
607         name_len = ncp->nc_nlen;
608         lhc = hammer2_dirhash(name, name_len);
609
610         /*
611          * Note: In DragonFly the kernel handles '.' and '..'.
612          */
613         parent = &dip->chain;
614         hammer2_chain_ref(hmp, parent);
615         hammer2_chain_lock(hmp, parent);
616         chain = hammer2_chain_lookup(hmp, &parent,
617                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
618                                      0);
619         while (chain) {
620                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
621                     chain->u.ip &&
622                     name_len == chain->data->ipdata.name_len &&
623                     bcmp(name, chain->data->ipdata.filename, name_len) == 0) {
624                         break;
625                 }
626                 chain = hammer2_chain_next(hmp, &parent, chain,
627                                            lhc, lhc + HAMMER2_DIRHASH_LOMASK,
628                                            0);
629         }
630         hammer2_chain_put(hmp, parent);
631
632         if (chain) {
633                 vp = hammer2_igetv(chain->u.ip, &error);
634                 if (error == 0) {
635                         vn_unlock(vp);
636                         cache_setvp(ap->a_nch, vp);
637                         vrele(vp);
638                 }
639                 hammer2_chain_put(hmp, chain);
640         } else {
641                 error = ENOENT;
642                 cache_setvp(ap->a_nch, NULL);
643         }
644         return error;
645 }
646
647 static
648 int
649 hammer2_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
650 {
651         hammer2_inode_t *dip;
652         hammer2_inode_t *ip;
653         hammer2_mount_t *hmp;
654         int error;
655
656         dip = VTOI(ap->a_dvp);
657         hmp = dip->hmp;
658
659         if ((ip = dip->pip) == NULL) {
660                 *ap->a_vpp = NULL;
661                 return ENOENT;
662         }
663         hammer2_chain_ref(hmp, &ip->chain);
664         hammer2_chain_lock(hmp, &ip->chain);
665         *ap->a_vpp = hammer2_igetv(ip, &error);
666         hammer2_chain_put(hmp, &ip->chain);
667
668         return error;
669 }
670
671 static
672 int
673 hammer2_vop_nmkdir(struct vop_nmkdir_args *ap)
674 {
675         hammer2_mount_t *hmp;
676         hammer2_inode_t *dip;
677         hammer2_inode_t *nip;
678         struct namecache *ncp;
679         const uint8_t *name;
680         size_t name_len;
681         int error;
682
683         dip = VTOI(ap->a_dvp);
684         hmp = dip->hmp;
685         if (hmp->ronly)
686                 return (EROFS);
687
688         ncp = ap->a_nch->ncp;
689         name = ncp->nc_name;
690         name_len = ncp->nc_nlen;
691
692         error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
693                                      dip, name, name_len, &nip);
694         if (error) {
695                 KKASSERT(nip == NULL);
696                 *ap->a_vpp = NULL;
697                 return error;
698         }
699         *ap->a_vpp = hammer2_igetv(nip, &error);
700         hammer2_chain_put(hmp, &nip->chain);
701
702         if (error == 0) {
703                 cache_setunresolved(ap->a_nch);
704                 cache_setvp(ap->a_nch, *ap->a_vpp);
705         }
706         return error;
707 }
708
709 /*
710  * Return the largest contiguous physical disk range for the logical
711  * request.
712  *
713  * (struct vnode *vp, off_t loffset, off_t *doffsetp, int *runp, int *runb)
714  */
715 static
716 int
717 hammer2_vop_bmap(struct vop_bmap_args *ap)
718 {
719         struct vnode *vp;
720         hammer2_mount_t *hmp;
721         hammer2_inode_t *ip;
722         hammer2_chain_t *parent;
723         hammer2_chain_t *chain;
724         hammer2_key_t loff;
725         hammer2_off_t poff;
726
727         /*
728          * Only supported on regular files
729          *
730          * Only supported for read operations (required for cluster_read).
731          * The block allocation is delayed for write operations.
732          */
733         vp = ap->a_vp;
734         if (vp->v_type != VREG)
735                 return (EOPNOTSUPP);
736         if (ap->a_cmd != BUF_CMD_READ)
737                 return (EOPNOTSUPP);
738
739         ip = VTOI(vp);
740         hmp = ip->hmp;
741
742         loff = ap->a_loffset;
743         KKASSERT((loff & HAMMER2_LBUFMASK64) == 0);
744
745         parent = &ip->chain;
746         hammer2_chain_ref(hmp, parent);
747         hammer2_chain_lock(hmp, parent);
748         chain = hammer2_chain_lookup(hmp, &parent, loff, loff, 0);
749         if (chain) {
750                 poff = loff - chain->bref.key +
751                        (chain->bref.data_off & HAMMER2_OFF_MASK);
752                 *ap->a_doffsetp = poff;
753                 hammer2_chain_put(hmp, chain);
754         } else {
755                 *ap->a_doffsetp = ZFOFFSET;     /* zero-fill hole */
756         }
757         hammer2_chain_put(hmp, parent);
758         return (0);
759 }
760
761 static
762 int
763 hammer2_vop_open(struct vop_open_args *ap)
764 {
765         return vop_stdopen(ap);
766 }
767
768 static
769 int
770 hammer2_vop_close(struct vop_close_args *ap)
771 {
772         return vop_stdclose(ap);
773 }
774
775 /*
776  * hammer_vop_ncreate { nch, dvp, vpp, cred, vap }
777  *
778  * The operating system has already ensured that the directory entry
779  * does not exist and done all appropriate namespace locking.
780  */
781 static
782 int
783 hammer2_vop_ncreate(struct vop_ncreate_args *ap)
784 {
785         hammer2_mount_t *hmp;
786         hammer2_inode_t *dip;
787         hammer2_inode_t *nip;
788         struct namecache *ncp;
789         const uint8_t *name;
790         size_t name_len;
791         int error;
792
793         dip = VTOI(ap->a_dvp);
794         hmp = dip->hmp;
795         if (hmp->ronly)
796                 return (EROFS);
797
798         ncp = ap->a_nch->ncp;
799         name = ncp->nc_name;
800         name_len = ncp->nc_nlen;
801
802         error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
803                                      dip, name, name_len, &nip);
804         if (error) {
805                 KKASSERT(nip == NULL);
806                 *ap->a_vpp = NULL;
807                 return error;
808         }
809         *ap->a_vpp = hammer2_igetv(nip, &error);
810         hammer2_chain_put(hmp, &nip->chain);
811
812         if (error == 0) {
813                 cache_setunresolved(ap->a_nch);
814                 cache_setvp(ap->a_nch, *ap->a_vpp);
815         }
816         return error;
817 }
818
819 static int hammer2_strategy_read(struct vop_strategy_args *ap);
820 static int hammer2_strategy_write(struct vop_strategy_args *ap);
821
822 static
823 int
824 hammer2_vop_strategy(struct vop_strategy_args *ap)
825 {
826         struct bio *biop;
827         struct buf *bp;
828         int error;
829
830         biop = ap->a_bio;
831         bp = biop->bio_buf;
832
833         switch(bp->b_cmd) {
834         case BUF_CMD_READ:
835                 error = hammer2_strategy_read(ap);
836                 break;
837         case BUF_CMD_WRITE:
838                 error = hammer2_strategy_write(ap);
839                 break;
840         default:
841                 bp->b_error = error = EINVAL;
842                 bp->b_flags |= B_ERROR;
843                 biodone(biop);
844                 break;
845         }
846
847         return (error);
848 }
849
850 static
851 int
852 hammer2_strategy_read(struct vop_strategy_args *ap)
853 {
854         struct buf *bp;
855         struct bio *bio;
856         struct bio *nbio;
857         hammer2_mount_t *hmp;
858         hammer2_inode_t *ip;
859         hammer2_chain_t *parent;
860         hammer2_chain_t *chain;
861         hammer2_key_t loff;
862         hammer2_off_t poff;
863
864         bio = ap->a_bio;
865         bp = bio->bio_buf;
866         ip = VTOI(ap->a_vp);
867         hmp = ip->hmp;
868         nbio = push_bio(bio);
869
870         if (nbio->bio_offset == NOOFFSET) {
871                 loff = bio->bio_offset;
872                 KKASSERT((loff & HAMMER2_LBUFMASK64) == 0);
873
874                 parent = &ip->chain;
875                 hammer2_chain_ref(hmp, parent);
876                 hammer2_chain_lock(hmp, parent);
877
878                 /*
879                  * Specifying NOLOCK avoids unnecessary bread()s of the
880                  * chain element's content.  We just need the block device
881                  * offset.
882                  */
883                 chain = hammer2_chain_lookup(hmp, &parent, loff, loff,
884                                              HAMMER2_LOOKUP_NOLOCK);
885                 if (chain) {
886                         poff = loff - chain->bref.key +
887                                (chain->bref.data_off & HAMMER2_OFF_MASK);
888                         nbio->bio_offset = poff;
889                         hammer2_chain_drop(hmp, chain);
890                 } else {
891                         nbio->bio_offset = ZFOFFSET;
892                 }
893                 hammer2_chain_put(hmp, parent);
894         }
895         if (nbio->bio_offset == ZFOFFSET) {
896                 bp->b_resid = 0;
897                 bp->b_error = 0;
898                 vfs_bio_clrbuf(bp);
899                 biodone(nbio);
900         } else {
901                 vn_strategy(hmp->devvp, nbio);
902         }
903         return (0);
904 }
905
906 static
907 int
908 hammer2_strategy_write(struct vop_strategy_args *ap)
909 {
910         struct buf *bp;
911         struct bio *bio;
912         struct bio *nbio;
913         hammer2_mount_t *hmp;
914         hammer2_inode_t *ip;
915         hammer2_chain_t *parent;
916         hammer2_chain_t *chain;
917         hammer2_key_t off_hi;
918         int off_lo;
919
920         bio = ap->a_bio;
921         bp = bio->bio_buf;
922         ip = VTOI(ap->a_vp);
923         hmp = ip->hmp;
924         nbio = push_bio(bio);
925
926         /*
927          * Our bmap doesn't support writes atm, and a vop_write should
928          * clear the physical disk offset cache for the copy-on-write
929          * operation.
930          */
931         KKASSERT(nbio->bio_offset == NOOFFSET);
932
933         off_hi = bio->bio_offset & HAMMER2_OFF_MASK_HI;
934         off_lo = bio->bio_offset & HAMMER2_OFF_MASK_LO;
935         KKASSERT((bio->bio_offset & HAMMER2_LBUFMASK64) == 0);
936
937         parent = &ip->chain;
938         hammer2_chain_ref(hmp, parent);
939         hammer2_chain_lock(hmp, parent);
940         chain = hammer2_chain_lookup(hmp, &parent, off_hi, off_hi, 0);
941         if (chain) {
942                 hammer2_chain_modify(hmp, chain);
943                 bcopy(bp->b_data, chain->data->buf + off_lo, bp->b_bcount);
944                 hammer2_chain_put(hmp, chain);
945         } else {
946                 chain = hammer2_chain_create(hmp, parent,
947                                              off_hi, HAMMER2_PBUFRADIX,
948                                              HAMMER2_BREF_TYPE_DATA,
949                                              HAMMER2_PBUFSIZE);
950                 bcopy(bp->b_data, chain->data->buf + off_lo, bp->b_bcount);
951                 hammer2_chain_put(hmp, chain);
952         }
953         hammer2_chain_put(hmp, parent);
954
955         bp->b_resid = 0;
956         bp->b_error = 0;
957         biodone(nbio);
958
959         return (0);
960 }
961
962 static
963 int 
964 hammer2_vop_mountctl(struct vop_mountctl_args *ap)
965 {
966         struct mount *mp;
967         struct hammer2_mount *hmp;
968         int rc;
969
970         switch (ap->a_op) {
971         case (MOUNTCTL_SET_EXPORT):
972                 mp = ap->a_head.a_ops->head.vv_mount;
973                 hmp = MPTOH2(mp);
974
975                 if (ap->a_ctllen != sizeof(struct export_args))
976                         rc = (EINVAL);
977                 else
978                         rc = vfs_export(mp, &hmp->export,
979                                         (const struct export_args *)ap->a_ctl);
980                 break;
981         default:
982                 rc = vop_stdmountctl(ap);
983                 break;
984         }
985         return (rc);
986 }
987
988 struct vop_ops hammer2_vnode_vops = {
989         .vop_default    = vop_defaultop,
990         .vop_fsync      = hammer2_vop_fsync,
991         .vop_getpages   = vop_stdgetpages,
992         .vop_putpages   = vop_stdputpages,
993         .vop_access     = hammer2_vop_access,
994         .vop_close      = hammer2_vop_close,
995         .vop_ncreate    = hammer2_vop_ncreate,
996         .vop_getattr    = hammer2_vop_getattr,
997         .vop_readdir    = hammer2_vop_readdir,
998         .vop_getpages   = vop_stdgetpages,
999         .vop_putpages   = vop_stdputpages,
1000         .vop_read       = hammer2_vop_read,
1001         .vop_write      = hammer2_vop_write,
1002         .vop_open       = hammer2_vop_open,
1003         .vop_inactive   = hammer2_vop_inactive,
1004         .vop_reclaim    = hammer2_vop_reclaim,
1005         .vop_nresolve   = hammer2_vop_nresolve,
1006         .vop_nlookupdotdot = hammer2_vop_nlookupdotdot,
1007         .vop_nmkdir     = hammer2_vop_nmkdir,
1008         .vop_mountctl   = hammer2_vop_mountctl,
1009         .vop_bmap       = hammer2_vop_bmap,
1010         .vop_strategy   = hammer2_vop_strategy,
1011 };
1012
1013 struct vop_ops hammer2_spec_vops = {
1014
1015 };
1016
1017 struct vop_ops hammer2_fifo_vops = {
1018
1019 };