hammer2 - Implement rename
[dragonfly.git] / sys / vfs / hammer2 / hammer2_vnops.c
CommitLineData
e118c14f
MD
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 */
703720e4
MD
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>
f0206a67 44#include <sys/mountctl.h>
e028fa74 45#include <sys/dirent.h>
4e2004ea 46#include <sys/uio.h>
703720e4
MD
47
48#include "hammer2.h"
49
db71f61f
MD
50#define ZFOFFSET (-2LL)
51
4e2004ea
MD
52static int hammer2_read_file(hammer2_inode_t *ip, struct uio *uio,
53 int seqcount);
54static int hammer2_write_file(hammer2_inode_t *ip, struct uio *uio, int ioflag);
3ac6a319
MD
55static void hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize,
56 int trivial);
57static void hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize);
4e2004ea
MD
58static int hammer2_unlink_file(hammer2_inode_t *dip,
59 const uint8_t *name, size_t name_len,
60 int isdir);
3ac6a319 61
703720e4
MD
62/*
63 * Last reference to a vnode is going away but it is still cached.
64 */
e118c14f 65static
703720e4 66int
e118c14f 67hammer2_vop_inactive(struct vop_inactive_args *ap)
703720e4
MD
68{
69 struct vnode *vp;
70 struct hammer2_inode *ip;
e118c14f 71#if 0
703720e4 72 struct hammer2_mount *hmp;
e118c14f 73#endif
703720e4 74
703720e4
MD
75 vp = ap->a_vp;
76 ip = VTOI(vp);
703720e4 77
df9ea374
MD
78 /*
79 * Degenerate case
80 */
81 if (ip == NULL) {
82 vrecycle(vp);
83 return (0);
84 }
85
703720e4
MD
86 return (0);
87}
88
89/*
90 * Reclaim a vnode so that it can be reused; after the inode is
91 * disassociated, the filesystem must manage it alone.
92 */
e118c14f 93static
703720e4 94int
e118c14f 95hammer2_vop_reclaim(struct vop_reclaim_args *ap)
703720e4 96{
703720e4
MD
97 struct hammer2_inode *ip;
98 struct hammer2_mount *hmp;
b7926f31 99 struct vnode *vp;
703720e4 100
703720e4
MD
101 vp = ap->a_vp;
102 ip = VTOI(vp);
9c2e0de0
MD
103 if (ip == NULL)
104 return(0);
9c2e0de0 105 hmp = ip->hmp;
b7926f31 106
54eb943b 107 hammer2_inode_lock_ex(ip);
703720e4 108 vp->v_data = NULL;
0e92b724 109 ip->vp = NULL;
b7926f31 110 hammer2_chain_flush(hmp, &ip->chain, NULL);
54eb943b 111 hammer2_inode_unlock_ex(ip);
9c2e0de0 112 hammer2_chain_drop(hmp, &ip->chain); /* vp ref removed */
54eb943b
MD
113
114 /*
115 * XXX handle background sync when ip dirty, kernel will no longer
116 * notify us regarding this inode because there is no longer a
117 * vnode attached to it.
118 */
703720e4
MD
119
120 return (0);
121}
122
e118c14f 123static
703720e4 124int
e118c14f 125hammer2_vop_fsync(struct vop_fsync_args *ap)
703720e4 126{
b7926f31
MD
127 struct hammer2_inode *ip;
128 struct hammer2_mount *hmp;
129 struct vnode *vp;
130
131 vp = ap->a_vp;
132 ip = VTOI(vp);
133 hmp = ip->hmp;
134
135 hammer2_inode_lock_ex(ip);
136 vfsync(vp, ap->a_waitfor, 1, NULL, NULL);
137 hammer2_chain_flush(hmp, &ip->chain, NULL);
138 hammer2_inode_unlock_ex(ip);
139 return (0);
703720e4
MD
140}
141
e118c14f 142static
703720e4 143int
e118c14f 144hammer2_vop_access(struct vop_access_args *ap)
703720e4 145{
37494cab
MD
146 hammer2_inode_t *ip = VTOI(ap->a_vp);
147 uid_t uid;
148 gid_t gid;
149 int error;
150
151 uid = hammer2_to_unix_xid(&ip->ip_data.uid);
152 gid = hammer2_to_unix_xid(&ip->ip_data.gid);
153
154 error = vop_helper_access(ap, uid, gid, ip->ip_data.mode,
155 ip->ip_data.uflags);
156 return (error);
703720e4
MD
157}
158
e118c14f 159static
703720e4 160int
e118c14f 161hammer2_vop_getattr(struct vop_getattr_args *ap)
703720e4 162{
cd4b3d92
MD
163 hammer2_mount_t *hmp;
164 hammer2_inode_t *ip;
703720e4
MD
165 struct vnode *vp;
166 struct vattr *vap;
703720e4
MD
167
168 vp = ap->a_vp;
169 vap = ap->a_vap;
170
cd4b3d92
MD
171 ip = VTOI(vp);
172 hmp = ip->hmp;
173
703720e4
MD
174 hammer2_inode_lock_sh(ip);
175
cd4b3d92
MD
176 vap->va_fsid = hmp->mp->mnt_stat.f_fsid.val[0];
177 vap->va_fileid = ip->ip_data.inum;
178 vap->va_mode = ip->ip_data.mode;
179 vap->va_nlink = ip->ip_data.nlinks;
703720e4
MD
180 vap->va_uid = 0;
181 vap->va_gid = 0;
cd4b3d92
MD
182 vap->va_rmajor = 0;
183 vap->va_rminor = 0;
184 vap->va_size = ip->ip_data.size;
df9ea374 185 vap->va_blocksize = HAMMER2_PBUFSIZE;
cd4b3d92
MD
186 vap->va_flags = ip->ip_data.uflags;
187 hammer2_time_to_timespec(ip->ip_data.ctime, &vap->va_ctime);
188 hammer2_time_to_timespec(ip->ip_data.mtime, &vap->va_mtime);
189 hammer2_time_to_timespec(ip->ip_data.mtime, &vap->va_atime);
190 vap->va_gen = 1;
191 vap->va_bytes = vap->va_size;
192 vap->va_type = hammer2_get_vtype(ip);
193 vap->va_filerev = 0;
194 vap->va_uid_uuid = ip->ip_data.uid;
195 vap->va_gid_uuid = ip->ip_data.gid;
196 vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
197 VA_FSID_UUID_VALID;
703720e4
MD
198
199 hammer2_inode_unlock_sh(ip);
200
201 return (0);
202}
203
3ac6a319
MD
204static
205int
206hammer2_vop_setattr(struct vop_setattr_args *ap)
207{
208 hammer2_mount_t *hmp;
209 hammer2_inode_t *ip;
210 struct vnode *vp;
211 struct vattr *vap;
212 int error;
213 int kflags = 0;
214 int doctime = 0;
215 int domtime = 0;
216
217 vp = ap->a_vp;
218 vap = ap->a_vap;
219
220 ip = VTOI(vp);
221 hmp = ip->hmp;
222
223 if (hmp->ronly)
224 return(EROFS);
225
226 hammer2_inode_lock_ex(ip);
227 error = 0;
228
229 if (vap->va_flags != VNOVAL) {
230 u_int32_t flags;
231
232 flags = ip->ip_data.uflags;
233 error = vop_helper_setattr_flags(&flags, vap->va_flags,
234 hammer2_to_unix_xid(&ip->ip_data.uid),
235 ap->a_cred);
236 if (error == 0) {
237 if (ip->ip_data.uflags != flags) {
238 hammer2_chain_modify(hmp, &ip->chain);
239 ip->ip_data.uflags = flags;
240 doctime = 1;
241 kflags |= NOTE_ATTRIB;
242 }
243 if (ip->ip_data.uflags & (IMMUTABLE | APPEND)) {
244 error = 0;
245 goto done;
246 }
247 }
248 }
249
250 if (ip->ip_data.uflags & (IMMUTABLE | APPEND)) {
251 error = EPERM;
252 goto done;
253 }
254 /* uid, gid */
255
256 /*
257 * Resize the file
258 */
259 if (vap->va_size != VNOVAL && ip->ip_data.size != vap->va_size) {
260 switch(vp->v_type) {
261 case VREG:
262 if (vap->va_size == ip->ip_data.size)
263 break;
264 if (vap->va_size < ip->ip_data.size) {
265 hammer2_chain_modify(hmp, &ip->chain);
266 hammer2_truncate_file(ip, vap->va_size);
267 ip->ip_data.size = vap->va_size;
268 } else {
269 hammer2_chain_modify(hmp, &ip->chain);
270 hammer2_extend_file(ip, vap->va_size, 0);
271 ip->ip_data.size = vap->va_size;
272 }
273 domtime = 1;
274 break;
275 default:
276 error = EINVAL;
277 goto done;
278 }
279 }
280done:
281 hammer2_inode_unlock_ex(ip);
282 return (error);
283}
284
e118c14f 285static
703720e4 286int
e118c14f 287hammer2_vop_readdir(struct vop_readdir_args *ap)
703720e4 288{
e028fa74
MD
289 hammer2_mount_t *hmp;
290 hammer2_inode_t *ip;
291 hammer2_inode_t *xip;
292 hammer2_chain_t *parent;
293 hammer2_chain_t *chain;
294 hammer2_key_t lkey;
295 struct uio *uio;
296 off_t *cookies;
297 off_t saveoff;
298 int cookie_index;
299 int ncookies;
300 int error;
301 int dtype;
302 int r;
303
304 ip = VTOI(ap->a_vp);
305 hmp = ip->hmp;
306 uio = ap->a_uio;
307 saveoff = uio->uio_offset;
308
309 /*
310 * Setup cookies directory entry cookies if requested
311 */
312 if (ap->a_ncookies) {
313 ncookies = uio->uio_resid / 16 + 1;
314 if (ncookies > 1024)
315 ncookies = 1024;
316 cookies = kmalloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK);
317 } else {
318 ncookies = -1;
319 cookies = NULL;
320 }
321 cookie_index = 0;
322
323 /*
324 * Handle artificial entries. To ensure that only positive 64 bit
325 * quantities are returned to userland we always strip off bit 63.
326 * The hash code is designed such that codes 0x0000-0x7FFF are not
327 * used, allowing us to use these codes for articial entries.
328 *
329 * Entry 0 is used for '.' and entry 1 is used for '..'. Do not
330 * allow '..' to cross the mount point into (e.g.) the super-root.
331 */
332 error = 0;
37aa19df 333 chain = (void *)(intptr_t)-1; /* non-NULL for early goto done case */
e028fa74
MD
334
335 if (saveoff == 0) {
336 r = vop_write_dirent(&error, uio,
337 ip->ip_data.inum &
338 HAMMER2_DIRHASH_USERMSK,
339 DT_DIR, 1, ".");
340 if (r)
341 goto done;
342 if (cookies)
343 cookies[cookie_index] = saveoff;
344 ++saveoff;
345 ++cookie_index;
346 if (cookie_index == ncookies)
347 goto done;
348 }
349 if (saveoff == 1) {
350 if (ip->pip == NULL || ip == hmp->iroot)
351 xip = ip;
352 else
353 xip = ip->pip;
354
355 r = vop_write_dirent(&error, uio,
356 xip->ip_data.inum &
357 HAMMER2_DIRHASH_USERMSK,
358 DT_DIR, 2, "..");
359 if (r)
360 goto done;
361 if (cookies)
362 cookies[cookie_index] = saveoff;
363 ++saveoff;
364 ++cookie_index;
365 if (cookie_index == ncookies)
366 goto done;
367 }
368
369 lkey = saveoff | HAMMER2_DIRHASH_VISIBLE;
370
371 parent = &ip->chain;
372 hammer2_chain_ref(hmp, parent);
373 error = hammer2_chain_lock(hmp, parent);
374 if (error) {
375 hammer2_chain_put(hmp, parent);
376 goto done;
377 }
37aa19df
MD
378 chain = hammer2_chain_lookup(hmp, &parent, lkey, lkey, 0);
379 if (chain == NULL) {
380 chain = hammer2_chain_lookup(hmp, &parent,
381 lkey, (hammer2_key_t)-1, 0);
382 }
e028fa74 383 while (chain) {
c667909f
MD
384 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
385 dtype = hammer2_get_dtype(chain->u.ip);
386 saveoff = chain->bref.key & HAMMER2_DIRHASH_USERMSK;
387 r = vop_write_dirent(&error, uio,
388 chain->u.ip->ip_data.inum &
389 HAMMER2_DIRHASH_USERMSK,
390 dtype, chain->u.ip->ip_data.name_len,
391 chain->u.ip->ip_data.filename);
392 if (r)
393 break;
394 if (cookies)
395 cookies[cookie_index] = saveoff;
396 ++cookie_index;
397 } else {
398 /* XXX chain error */
399 kprintf("bad chain type readdir %d\n",
400 chain->bref.type);
401 }
995e78dc
MD
402
403 /*
404 * Keys may not be returned in order so once we have a
405 * placemarker (chain) the scan must allow the full range
406 * or some entries will be missed.
407 */
e028fa74 408 chain = hammer2_chain_next(hmp, &parent, chain,
995e78dc 409 0, (hammer2_key_t)-1, 0);
028a55bb
MD
410 if (chain) {
411 saveoff = (chain->bref.key &
412 HAMMER2_DIRHASH_USERMSK) + 1;
413 } else {
414 saveoff = (hammer2_key_t)-1;
415 }
416 if (cookie_index == ncookies)
417 break;
e028fa74
MD
418 }
419 hammer2_chain_put(hmp, parent);
028a55bb
MD
420 if (chain)
421 hammer2_chain_put(hmp, chain);
e028fa74
MD
422done:
423 if (ap->a_eofflag)
424 *ap->a_eofflag = (chain == NULL);
37aa19df 425 uio->uio_offset = saveoff & ~HAMMER2_DIRHASH_VISIBLE;
e028fa74
MD
426 if (error && cookie_index == 0) {
427 if (cookies) {
428 kfree(cookies, M_TEMP);
429 *ap->a_ncookies = 0;
430 *ap->a_cookies = NULL;
431 }
432 } else {
433 if (cookies) {
434 *ap->a_ncookies = cookie_index;
435 *ap->a_cookies = cookies;
436 }
437 }
438 return (error);
703720e4
MD
439}
440
4e2004ea
MD
441/*
442 * hammer2_vop_readlink { vp, uio, cred }
443 */
444static
445int
446hammer2_vop_readlink(struct vop_readlink_args *ap)
447{
448 struct vnode *vp;
449 hammer2_mount_t *hmp;
450 hammer2_inode_t *ip;
451 int error;
452
453 vp = ap->a_vp;
454 if (vp->v_type != VLNK)
455 return (EINVAL);
456 ip = VTOI(vp);
457 hmp = ip->hmp;
458
459 error = hammer2_read_file(ip, ap->a_uio, 0);
460 return (error);
461}
462
e118c14f 463static
703720e4 464int
e118c14f 465hammer2_vop_read(struct vop_read_args *ap)
703720e4 466{
db71f61f
MD
467 struct vnode *vp;
468 hammer2_mount_t *hmp;
469 hammer2_inode_t *ip;
db71f61f
MD
470 struct uio *uio;
471 int error;
472 int seqcount;
473 int bigread;
474
475 /*
476 * Read operations supported on this vnode?
477 */
478 vp = ap->a_vp;
479 if (vp->v_type != VREG)
480 return (EINVAL);
481
482 /*
483 * Misc
484 */
485 ip = VTOI(vp);
486 hmp = ip->hmp;
487 uio = ap->a_uio;
488 error = 0;
489
490 seqcount = ap->a_ioflag >> 16;
491 bigread = (uio->uio_resid > 100 * 1024 * 1024);
492
4e2004ea 493 error = hammer2_read_file(ip, uio, seqcount);
db71f61f 494 return (error);
47902fef 495}
703720e4 496
e118c14f 497static
47902fef 498int
e118c14f 499hammer2_vop_write(struct vop_write_args *ap)
47902fef 500{
db71f61f
MD
501 thread_t td;
502 struct vnode *vp;
503 hammer2_mount_t *hmp;
504 hammer2_inode_t *ip;
db71f61f
MD
505 struct uio *uio;
506 int error;
db71f61f
MD
507 int seqcount;
508 int bigwrite;
509
510 /*
511 * Read operations supported on this vnode?
512 */
513 vp = ap->a_vp;
514 if (vp->v_type != VREG)
515 return (EINVAL);
516
517 /*
518 * Misc
519 */
520 ip = VTOI(vp);
521 hmp = ip->hmp;
522 uio = ap->a_uio;
523 error = 0;
db71f61f
MD
524 if (hmp->ronly)
525 return (EROFS);
526
527 seqcount = ap->a_ioflag >> 16;
528 bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
529
530 /*
531 * Check resource limit
532 */
533 if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc &&
534 uio->uio_offset + uio->uio_resid >
535 td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
536 lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
537 return (EFBIG);
538 }
539
540 bigwrite = (uio->uio_resid > 100 * 1024 * 1024);
541
542 /*
3ac6a319
MD
543 * ip must be locked if extending the file.
544 * ip must be locked to avoid racing a truncation.
545 */
546 hammer2_inode_lock_ex(ip);
547 hammer2_chain_modify(hmp, &ip->chain);
4e2004ea
MD
548 error = hammer2_write_file(ip, uio, ap->a_ioflag);
549
550 hammer2_inode_unlock_ex(ip);
551 return (error);
552}
553
554/*
555 * Perform read operations on a file or symlink given an UNLOCKED
556 * inode and uio.
557 */
558static
559int
560hammer2_read_file(hammer2_inode_t *ip, struct uio *uio, int seqcount)
561{
562 struct buf *bp;
563 int error;
564
565 error = 0;
566
567 /*
568 * UIO read loop
569 */
570 while (uio->uio_resid > 0 && uio->uio_offset < ip->ip_data.size) {
571 hammer2_key_t off_hi;
572 int off_lo;
573 int n;
574
575 off_hi = uio->uio_offset & ~HAMMER2_LBUFMASK64;
576 off_lo = (int)(uio->uio_offset & HAMMER2_LBUFMASK64);
577
578 /* XXX bigread & signal check test */
579
580 error = cluster_read(ip->vp, ip->ip_data.size, off_hi,
581 HAMMER2_LBUFSIZE, HAMMER2_PBUFSIZE,
582 seqcount * BKVASIZE, &bp);
583 if (error)
584 break;
585 n = HAMMER2_LBUFSIZE - off_lo;
586 if (n > uio->uio_resid)
587 n = uio->uio_resid;
588 if (n > ip->ip_data.size - uio->uio_offset)
589 n = (int)(ip->ip_data.size - uio->uio_offset);
590 bp->b_flags |= B_AGE;
591 uiomove((char *)bp->b_data + off_lo, n, uio);
592 bqrelse(bp);
593 }
594 return (error);
595}
596
597/*
598 * Called with a locked (ip) to do the underlying write to a file or
599 * to build the symlink target.
600 */
601static
602int
603hammer2_write_file(hammer2_inode_t *ip, struct uio *uio, int ioflag)
604{
605 struct buf *bp;
606 int kflags;
607 int error;
3ac6a319 608
4e2004ea
MD
609 /*
610 * Setup if append
611 */
612 if (ioflag & IO_APPEND)
3ac6a319 613 uio->uio_offset = ip->ip_data.size;
4e2004ea
MD
614 kflags = 0;
615 error = 0;
3ac6a319
MD
616
617 /*
618 * UIO write loop
db71f61f
MD
619 */
620 while (uio->uio_resid > 0) {
621 hammer2_key_t nsize;
622 hammer2_key_t off_hi;
623 int fixsize;
624 int off_lo;
625 int n;
626 int trivial;
627 int endofblk;
628
629 off_hi = uio->uio_offset & ~HAMMER2_LBUFMASK64;
630 off_lo = (int)(uio->uio_offset & HAMMER2_LBUFMASK64);
631
632 n = HAMMER2_LBUFSIZE - off_lo;
633 if (n > uio->uio_resid) {
634 n = uio->uio_resid;
635 endofblk = 0;
636 } else {
637 endofblk = 1;
638 }
639 nsize = uio->uio_offset + n;
640
641 /* XXX bigwrite & signal check test */
642
643 /*
644 * Don't allow the buffer build to blow out the buffer
645 * cache.
646 */
4e2004ea 647 if ((ioflag & IO_RECURSE) == 0)
db71f61f
MD
648 bwillwrite(HAMMER2_LBUFSIZE);
649
650 /*
651 * Extend the size of the file as needed
652 * XXX lock.
653 */
654 if (nsize > ip->ip_data.size) {
655 if (uio->uio_offset > ip->ip_data.size)
656 trivial = 0;
657 else
658 trivial = 1;
3ac6a319 659 hammer2_extend_file(ip, nsize, trivial);
db71f61f
MD
660 kflags |= NOTE_EXTEND;
661 fixsize = 1;
662 } else {
663 fixsize = 0;
664 }
665
666 if (uio->uio_segflg == UIO_NOCOPY) {
667 /*
668 * Issuing a write with the same data backing the
669 * buffer. Instantiate the buffer to collect the
670 * backing vm pages, then read-in any missing bits.
671 *
672 * This case is used by vop_stdputpages().
673 */
4e2004ea 674 bp = getblk(ip->vp, off_hi,
db71f61f
MD
675 HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
676 if ((bp->b_flags & B_CACHE) == 0) {
677 bqrelse(bp);
4e2004ea
MD
678 error = bread(ip->vp, off_hi,
679 HAMMER2_LBUFSIZE, &bp);
db71f61f
MD
680 }
681 } else if (off_lo == 0 && uio->uio_resid >= HAMMER2_LBUFSIZE) {
682 /*
683 * Even though we are entirely overwriting the buffer
684 * we may still have to zero it out to avoid a
685 * mmap/write visibility issue.
686 */
4e2004ea 687 bp = getblk(ip->vp, off_hi,
db71f61f
MD
688 HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
689 if ((bp->b_flags & B_CACHE) == 0)
690 vfs_bio_clrbuf(bp);
691 } else if (off_hi >= ip->ip_data.size) {
692 /*
693 * If the base offset of the buffer is beyond the
694 * file EOF, we don't have to issue a read.
695 */
4e2004ea 696 bp = getblk(ip->vp, off_hi,
db71f61f
MD
697 HAMMER2_LBUFSIZE, GETBLK_BHEAVY, 0);
698 vfs_bio_clrbuf(bp);
699 } else {
700 /*
701 * Partial overwrite, read in any missing bits then
702 * replace the portion being written.
703 */
4e2004ea 704 error = bread(ip->vp, off_hi, HAMMER2_LBUFSIZE, &bp);
db71f61f
MD
705 if (error == 0)
706 bheavy(bp);
707 }
708
709 if (error == 0) {
710 /* release lock */
711 error = uiomove(bp->b_data + off_lo, n, uio);
712 /* acquire lock */
713 }
714
715 if (error) {
716 brelse(bp);
3ac6a319
MD
717 if (fixsize)
718 hammer2_truncate_file(ip, ip->ip_data.size);
db71f61f
MD
719 break;
720 }
721 kflags |= NOTE_WRITE;
722 if (ip->ip_data.size < uio->uio_offset)
723 ip->ip_data.size = uio->uio_offset;
724 /* XXX update ino_data.mtime */
725
726 /*
727 * Once we dirty a buffer any cached offset becomes invalid.
728 */
729 bp->b_bio2.bio_offset = NOOFFSET;
730 bp->b_flags |= B_AGE;
4e2004ea 731 if (ioflag & IO_SYNC) {
db71f61f 732 bwrite(bp);
4e2004ea 733 } else if ((ioflag & IO_DIRECT) && endofblk) {
db71f61f 734 bawrite(bp);
4e2004ea 735 } else if (ioflag & IO_ASYNC) {
db71f61f
MD
736 bawrite(bp);
737 } else {
738 bdwrite(bp);
739 }
740 }
4e2004ea
MD
741 /* hammer2_knote(ip->vp, kflags); */
742 return error;
703720e4
MD
743}
744
3ac6a319
MD
745/*
746 * Truncate the size of a file. The inode must be locked and marked
747 * for modification. The caller will set ip->ip_data.size after we
748 * return, we do not do it ourselves.
749 */
750static
751void
752hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
753{
754 hammer2_chain_t *parent;
755 hammer2_chain_t *chain;
756 hammer2_mount_t *hmp = ip->hmp;
757 hammer2_key_t psize;
758 int error;
759
760 /*
761 * Destroy any logical buffer cache buffers beyond the file EOF
762 * and partially clean out any straddling buffer.
763 */
764 if (ip->vp) {
765 nvtruncbuf(ip->vp, nsize,
766 HAMMER2_LBUFSIZE, nsize & HAMMER2_LBUFMASK);
767 }
768 nsize = (nsize + HAMMER2_LBUFMASK64) & ~HAMMER2_LBUFMASK64;
769
770 /*
771 * Setup for lookup/next
772 */
773 parent = &ip->chain;
774 hammer2_chain_ref(hmp, parent);
775 error = hammer2_chain_lock(hmp, parent);
776 if (error) {
777 hammer2_chain_put(hmp, parent);
778 /* XXX error reporting */
779 return;
780 }
781
782 /*
783 * Calculate the first physical buffer beyond the new file EOF.
784 * The straddling physical buffer will be at (psize - PBUFSIZE).
785 */
786 psize = (nsize + HAMMER2_PBUFMASK64) & ~HAMMER2_PBUFMASK64;
787
788 if (nsize != psize) {
789 KKASSERT(psize >= HAMMER2_PBUFSIZE64);
790 chain = hammer2_chain_lookup(hmp, &parent,
791 psize - HAMMER2_PBUFSIZE,
792 psize - HAMMER2_PBUFSIZE, 0);
793 if (chain) {
794 if (chain->bref.type == HAMMER2_BREF_TYPE_DATA) {
795 hammer2_chain_modify(hmp, chain);
796 bzero(chain->data->buf +
797 (int)(nsize & HAMMER2_PBUFMASK64),
798 (size_t)(psize - nsize));
799 kprintf("ZEROBIGBOY %08x/%zd\n",
800 (int)(nsize & HAMMER2_PBUFMASK64),
801 (size_t)(psize - nsize));
802 }
803 hammer2_chain_put(hmp, chain);
804 }
805 }
806
807 chain = hammer2_chain_lookup(hmp, &parent,
808 psize, (hammer2_key_t)-1,
809 HAMMER2_LOOKUP_NOLOCK);
810 while (chain) {
811 /*
812 * Degenerate embedded data case, nothing to loop on.
813 */
814 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
815 break;
816
817 /*
818 * Delete physical data blocks past the file EOF.
819 */
820 if (chain->bref.type == HAMMER2_BREF_TYPE_DATA) {
821 hammer2_chain_delete(hmp, parent, chain);
822 }
823 chain = hammer2_chain_next(hmp, &parent, chain,
824 psize, (hammer2_key_t)-1,
825 HAMMER2_LOOKUP_NOLOCK);
826 }
827 hammer2_chain_put(hmp, parent);
828}
829
830/*
831 * Extend the size of a file. The inode must be locked and marked
832 * for modification. The caller will set ip->ip_data.size after we
833 * return, we do not do it ourselves.
834 */
835static
836void
837hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize, int trivial)
838{
839 struct buf *bp;
840 int error;
841
842 /*
843 * Turn off the embedded-data-in-inode feature if the file size
844 * extends past the embedded limit. To keep things simple this
845 * feature is never re-enabled once disabled.
846 */
847 if ((ip->ip_data.op_flags & HAMMER2_OPFLAG_DIRECTDATA) &&
848 nsize > HAMMER2_EMBEDDED_BYTES) {
849 error = bread(ip->vp, 0, HAMMER2_LBUFSIZE, &bp);
850 KKASSERT(error == 0);
851 ip->ip_data.op_flags &= ~HAMMER2_OPFLAG_DIRECTDATA;
852 bzero(&ip->ip_data.u.blockset,
853 sizeof(ip->ip_data.u.blockset));
854 bdwrite(bp);
855 }
856 if (ip->vp) {
857 nvextendbuf(ip->vp, ip->ip_data.size, nsize,
858 HAMMER2_LBUFSIZE, HAMMER2_LBUFSIZE,
859 (int)(ip->ip_data.size & HAMMER2_LBUFMASK),
860 (int)(nsize & HAMMER2_LBUFMASK),
861 trivial);
862 }
863}
864
e118c14f 865static
703720e4 866int
e118c14f 867hammer2_vop_nresolve(struct vop_nresolve_args *ap)
703720e4 868{
37494cab
MD
869 hammer2_inode_t *dip;
870 hammer2_mount_t *hmp;
871 hammer2_chain_t *parent;
872 hammer2_chain_t *chain;
873 struct namecache *ncp;
874 const uint8_t *name;
875 size_t name_len;
876 hammer2_key_t lhc;
877 int error = 0;
878 struct vnode *vp;
879
880 dip = VTOI(ap->a_dvp);
881 hmp = dip->hmp;
882 ncp = ap->a_nch->ncp;
883 name = ncp->nc_name;
884 name_len = ncp->nc_nlen;
885 lhc = hammer2_dirhash(name, name_len);
886
887 /*
888 * Note: In DragonFly the kernel handles '.' and '..'.
889 */
890 parent = &dip->chain;
891 hammer2_chain_ref(hmp, parent);
892 hammer2_chain_lock(hmp, parent);
893 chain = hammer2_chain_lookup(hmp, &parent,
c667909f
MD
894 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
895 0);
37494cab
MD
896 while (chain) {
897 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
898 chain->u.ip &&
899 name_len == chain->data->ipdata.name_len &&
900 bcmp(name, chain->data->ipdata.filename, name_len) == 0) {
901 break;
902 }
903 chain = hammer2_chain_next(hmp, &parent, chain,
c667909f
MD
904 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
905 0);
37494cab
MD
906 }
907 hammer2_chain_put(hmp, parent);
908
909 if (chain) {
910 vp = hammer2_igetv(chain->u.ip, &error);
911 if (error == 0) {
912 vn_unlock(vp);
913 cache_setvp(ap->a_nch, vp);
914 vrele(vp);
915 }
916 hammer2_chain_put(hmp, chain);
917 } else {
918 error = ENOENT;
919 cache_setvp(ap->a_nch, NULL);
920 }
921 return error;
922}
923
924static
925int
926hammer2_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
927{
928 hammer2_inode_t *dip;
929 hammer2_inode_t *ip;
930 hammer2_mount_t *hmp;
931 int error;
932
933 dip = VTOI(ap->a_dvp);
934 hmp = dip->hmp;
935
936 if ((ip = dip->pip) == NULL) {
937 *ap->a_vpp = NULL;
938 return ENOENT;
939 }
940 hammer2_chain_ref(hmp, &ip->chain);
941 hammer2_chain_lock(hmp, &ip->chain);
942 *ap->a_vpp = hammer2_igetv(ip, &error);
943 hammer2_chain_put(hmp, &ip->chain);
944
945 return error;
946}
947
948static
949int
950hammer2_vop_nmkdir(struct vop_nmkdir_args *ap)
951{
952 hammer2_mount_t *hmp;
953 hammer2_inode_t *dip;
954 hammer2_inode_t *nip;
955 struct namecache *ncp;
956 const uint8_t *name;
957 size_t name_len;
958 int error;
959
960 dip = VTOI(ap->a_dvp);
961 hmp = dip->hmp;
db71f61f
MD
962 if (hmp->ronly)
963 return (EROFS);
964
37494cab
MD
965 ncp = ap->a_nch->ncp;
966 name = ncp->nc_name;
967 name_len = ncp->nc_nlen;
968
969 error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
970 dip, name, name_len, &nip);
971 if (error) {
972 KKASSERT(nip == NULL);
973 *ap->a_vpp = NULL;
974 return error;
975 }
976 *ap->a_vpp = hammer2_igetv(nip, &error);
977 hammer2_chain_put(hmp, &nip->chain);
978
979 if (error == 0) {
980 cache_setunresolved(ap->a_nch);
981 cache_setvp(ap->a_nch, *ap->a_vpp);
982 }
983 return error;
703720e4
MD
984}
985
db71f61f
MD
986/*
987 * Return the largest contiguous physical disk range for the logical
988 * request.
989 *
990 * (struct vnode *vp, off_t loffset, off_t *doffsetp, int *runp, int *runb)
991 */
e118c14f 992static
703720e4 993int
e118c14f 994hammer2_vop_bmap(struct vop_bmap_args *ap)
703720e4 995{
db71f61f
MD
996 struct vnode *vp;
997 hammer2_mount_t *hmp;
998 hammer2_inode_t *ip;
999 hammer2_chain_t *parent;
1000 hammer2_chain_t *chain;
5b4a2132
MD
1001 hammer2_key_t loff;
1002 hammer2_off_t poff;
db71f61f
MD
1003
1004 /*
1005 * Only supported on regular files
1006 *
1007 * Only supported for read operations (required for cluster_read).
1008 * The block allocation is delayed for write operations.
1009 */
1010 vp = ap->a_vp;
1011 if (vp->v_type != VREG)
1012 return (EOPNOTSUPP);
1013 if (ap->a_cmd != BUF_CMD_READ)
1014 return (EOPNOTSUPP);
1015
1016 ip = VTOI(vp);
1017 hmp = ip->hmp;
5b4a2132
MD
1018
1019 loff = ap->a_loffset;
1020 KKASSERT((loff & HAMMER2_LBUFMASK64) == 0);
db71f61f
MD
1021
1022 parent = &ip->chain;
1023 hammer2_chain_ref(hmp, parent);
1024 hammer2_chain_lock(hmp, parent);
5b4a2132 1025 chain = hammer2_chain_lookup(hmp, &parent, loff, loff, 0);
3ac6a319
MD
1026 if (chain == NULL) {
1027 /*
1028 * zero-fill hole
1029 */
1030 *ap->a_doffsetp = ZFOFFSET;
1031 } else if (chain->bref.type == HAMMER2_BREF_TYPE_DATA) {
1032 /*
1033 * Normal data ref
1034 */
5b4a2132
MD
1035 poff = loff - chain->bref.key +
1036 (chain->bref.data_off & HAMMER2_OFF_MASK);
1037 *ap->a_doffsetp = poff;
db71f61f
MD
1038 hammer2_chain_put(hmp, chain);
1039 } else {
3ac6a319
MD
1040 /*
1041 * Data is embedded in inode, no direct I/O possible.
1042 */
1043 *ap->a_doffsetp = NOOFFSET;
1044 hammer2_chain_put(hmp, chain);
db71f61f
MD
1045 }
1046 hammer2_chain_put(hmp, parent);
1047 return (0);
703720e4
MD
1048}
1049
e118c14f 1050static
703720e4 1051int
e118c14f 1052hammer2_vop_open(struct vop_open_args *ap)
703720e4 1053{
703720e4
MD
1054 return vop_stdopen(ap);
1055}
1056
37aa19df
MD
1057/*
1058 * hammer_vop_advlock { vp, id, op, fl, flags }
1059 *
1060 * MPSAFE - does not require fs_token
1061 */
1062static
1063int
1064hammer2_vop_advlock(struct vop_advlock_args *ap)
1065{
1066 hammer2_inode_t *ip = VTOI(ap->a_vp);
1067
1068 return (lf_advlock(ap, &ip->advlock, ip->ip_data.size));
1069}
1070
1071
c667909f
MD
1072static
1073int
1074hammer2_vop_close(struct vop_close_args *ap)
1075{
1076 return vop_stdclose(ap);
1077}
1078
1079/*
1080 * hammer_vop_ncreate { nch, dvp, vpp, cred, vap }
1081 *
1082 * The operating system has already ensured that the directory entry
1083 * does not exist and done all appropriate namespace locking.
1084 */
1085static
1086int
1087hammer2_vop_ncreate(struct vop_ncreate_args *ap)
1088{
1089 hammer2_mount_t *hmp;
1090 hammer2_inode_t *dip;
1091 hammer2_inode_t *nip;
1092 struct namecache *ncp;
1093 const uint8_t *name;
1094 size_t name_len;
1095 int error;
1096
1097 dip = VTOI(ap->a_dvp);
1098 hmp = dip->hmp;
1099 if (hmp->ronly)
1100 return (EROFS);
1101
1102 ncp = ap->a_nch->ncp;
1103 name = ncp->nc_name;
1104 name_len = ncp->nc_nlen;
1105
1106 error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
1107 dip, name, name_len, &nip);
1108 if (error) {
1109 KKASSERT(nip == NULL);
1110 *ap->a_vpp = NULL;
1111 return error;
1112 }
1113 *ap->a_vpp = hammer2_igetv(nip, &error);
1114 hammer2_chain_put(hmp, &nip->chain);
1115
1116 if (error == 0) {
1117 cache_setunresolved(ap->a_nch);
1118 cache_setvp(ap->a_nch, *ap->a_vpp);
1119 }
1120 return error;
1121}
1122
4e2004ea
MD
1123/*
1124 * hammer2_vop_nsymlink { nch, dvp, vpp, cred, vap, target }
1125 */
1126static
1127int
1128hammer2_vop_nsymlink(struct vop_nsymlink_args *ap)
1129{
1130 hammer2_mount_t *hmp;
1131 hammer2_inode_t *dip;
1132 hammer2_inode_t *nip;
1133 struct namecache *ncp;
1134 const uint8_t *name;
1135 size_t name_len;
1136 int error;
1137
1138 dip = VTOI(ap->a_dvp);
1139 hmp = dip->hmp;
1140 if (hmp->ronly)
1141 return (EROFS);
1142
1143 ncp = ap->a_nch->ncp;
1144 name = ncp->nc_name;
1145 name_len = ncp->nc_nlen;
1146
1147 ap->a_vap->va_type = VLNK; /* enforce type */
1148
1149 error = hammer2_create_inode(hmp, ap->a_vap, ap->a_cred,
1150 dip, name, name_len, &nip);
1151 if (error) {
1152 KKASSERT(nip == NULL);
1153 *ap->a_vpp = NULL;
1154 return error;
1155 }
1156 *ap->a_vpp = hammer2_igetv(nip, &error);
1157
1158 /*
1159 * Build the softlink (~like file data) and finalize the namecache.
1160 */
1161 if (error == 0) {
1162 size_t bytes;
1163 struct uio auio;
1164 struct iovec aiov;
1165
1166 bytes = strlen(ap->a_target);
1167
1168 if (bytes <= HAMMER2_EMBEDDED_BYTES) {
1169 KKASSERT(nip->ip_data.op_flags &
1170 HAMMER2_OPFLAG_DIRECTDATA);
1171 bcopy(ap->a_target, nip->ip_data.u.data, bytes);
1172 nip->ip_data.size = bytes;
1173 } else {
1174 bzero(&auio, sizeof(auio));
1175 bzero(&aiov, sizeof(aiov));
1176 auio.uio_iov = &aiov;
1177 auio.uio_segflg = UIO_SYSSPACE;
1178 auio.uio_rw = UIO_WRITE;
1179 auio.uio_resid = bytes;
1180 auio.uio_iovcnt = 1;
1181 auio.uio_td = curthread;
1182 aiov.iov_base = ap->a_target;
1183 aiov.iov_len = bytes;
1184 error = hammer2_write_file(nip, &auio, IO_APPEND);
1185 /* XXX handle error */
1186 error = 0;
1187 }
1188 }
1189 hammer2_chain_put(hmp, &nip->chain);
1190
1191 /*
1192 * Finalize namecache
1193 */
1194 if (error == 0) {
1195 cache_setunresolved(ap->a_nch);
1196 cache_setvp(ap->a_nch, *ap->a_vpp);
1197 /* hammer2_knote(ap->a_dvp, NOTE_WRITE); */
1198 }
1199 return error;
1200}
1201
1202/*
1203 * hammer2_vop_nremove { nch, dvp, cred }
1204 */
1205static
1206int
1207hammer2_vop_nremove(struct vop_nremove_args *ap)
1208{
1209 hammer2_inode_t *dip;
1210 hammer2_mount_t *hmp;
1211 struct namecache *ncp;
1212 const uint8_t *name;
1213 size_t name_len;
1214 int error;
1215
1216 dip = VTOI(ap->a_dvp);
1217 hmp = dip->hmp;
1218 if (hmp->ronly)
1219 return(EROFS);
1220
1221 ncp = ap->a_nch->ncp;
1222 name = ncp->nc_name;
1223 name_len = ncp->nc_nlen;
1224
1225 error = hammer2_unlink_file(dip, name, name_len, 0);
1226
1227 if (error == 0) {
1228 cache_setunresolved(ap->a_nch);
1229 cache_setvp(ap->a_nch, NULL);
1230 }
1231 return (error);
1232}
1233
1234/*
1235 * hammer2_vop_nrmdir { nch, dvp, cred }
1236 */
1237static
1238int
1239hammer2_vop_nrmdir(struct vop_nrmdir_args *ap)
1240{
1241 hammer2_inode_t *dip;
1242 hammer2_mount_t *hmp;
1243 struct namecache *ncp;
1244 const uint8_t *name;
1245 size_t name_len;
1246 int error;
1247
1248 dip = VTOI(ap->a_dvp);
1249 hmp = dip->hmp;
1250 if (hmp->ronly)
1251 return(EROFS);
1252
1253 ncp = ap->a_nch->ncp;
1254 name = ncp->nc_name;
1255 name_len = ncp->nc_nlen;
1256
1257 error = hammer2_unlink_file(dip, name, name_len, 1);
1258
1259 if (error == 0) {
1260 cache_setunresolved(ap->a_nch);
1261 cache_setvp(ap->a_nch, NULL);
1262 }
1263 return (error);
1264}
1265
6934ae32
MD
1266/*
1267 * hammer2_vop_nrename { fnch, tnch, fdvp, tdvp, cred }
1268 */
4e2004ea
MD
1269static
1270int
1271hammer2_vop_nrename(struct vop_nrename_args *ap)
1272{
6934ae32
MD
1273 struct namecache *fncp;
1274 struct namecache *tncp;
1275 hammer2_inode_t *fdip;
1276 hammer2_inode_t *tdip;
1277 hammer2_inode_t *ip;
1278 hammer2_mount_t *hmp;
1279 const uint8_t *fname;
1280 size_t fname_len;
1281 const uint8_t *tname;
1282 size_t tname_len;
1283 int error;
1284
1285 if (ap->a_fdvp->v_mount != ap->a_tdvp->v_mount)
1286 return(EXDEV);
1287 if (ap->a_fdvp->v_mount != ap->a_fnch->ncp->nc_vp->v_mount)
1288 return(EXDEV);
1289
1290 fdip = VTOI(ap->a_fdvp); /* source directory */
1291 tdip = VTOI(ap->a_tdvp); /* target directory */
1292
1293 hmp = fdip->hmp; /* check read-only filesystem */
1294 if (hmp->ronly)
1295 return(EROFS);
1296
1297 fncp = ap->a_fnch->ncp; /* entry name in source */
1298 fname = fncp->nc_name;
1299 fname_len = fncp->nc_nlen;
1300
1301 tncp = ap->a_tnch->ncp; /* entry name in target */
1302 tname = tncp->nc_name;
1303 tname_len = tncp->nc_nlen;
1304
1305 ip = VTOI(fncp->nc_vp); /* inode being moved */
1306
1307 /*
1308 * Keep a tight grip on the inode as removing it should disconnect
1309 * it and we don't want to destroy it.
1310 */
1311 hammer2_chain_ref(hmp, &ip->chain);
1312 hammer2_chain_lock(hmp, &ip->chain);
1313
1314 /*
1315 * Remove target if it exists
1316 */
1317 error = hammer2_unlink_file(tdip, tname, tname_len, -1);
1318 if (error && error != ENOENT)
1319 goto done;
1320 cache_setunresolved(ap->a_tnch);
1321 cache_setvp(ap->a_tnch, NULL);
1322
1323 /*
1324 * Disconnect ip from the source directory.
1325 */
1326 error = hammer2_unlink_file(fdip, fname, fname_len, -1);
1327 if (error)
1328 goto done;
1329
1330 if (ip->chain.parent != NULL)
1331 panic("hammer2_vop_nrename(): rename source != ip!");
1332
1333 /*
1334 * Reconnect ip to target directory.
1335 */
1336 error = hammer2_connect_inode(tdip, ip, tname, tname_len);
1337
1338 if (error == 0) {
1339 cache_rename(ap->a_fnch, ap->a_tnch);
1340 }
1341done:
1342 hammer2_chain_unlock(hmp, &ip->chain);
1343 hammer2_chain_drop(hmp, &ip->chain);
1344
1345 return (error);
4e2004ea
MD
1346}
1347
6934ae32
MD
1348/*
1349 * Unlink the file from the specified directory inode. The directory inode
1350 * does not need to be locked.
1351 */
4e2004ea
MD
1352static
1353int
1354hammer2_unlink_file(hammer2_inode_t *dip, const uint8_t *name, size_t name_len,
1355 int isdir)
1356{
1357 hammer2_mount_t *hmp;
1358 hammer2_chain_t *parent;
1359 hammer2_chain_t *chain;
1360 hammer2_chain_t *dparent;
1361 hammer2_chain_t *dchain;
1362 hammer2_key_t lhc;
1363 struct vnode *vp;
1364 int error;
1365
1366 error = 0;
1367 /*vap->va_nlink = ip->ip_data.nlinks;*/
1368
1369 hmp = dip->hmp;
1370 lhc = hammer2_dirhash(name, name_len);
1371
1372 /*
1373 * Search for the filename in the directory
1374 */
1375 parent = &dip->chain;
1376 hammer2_chain_ref(hmp, parent);
1377 hammer2_chain_lock(hmp, parent);
1378 chain = hammer2_chain_lookup(hmp, &parent,
1379 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
1380 0);
1381 while (chain) {
1382 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
1383 chain->u.ip &&
1384 name_len == chain->data->ipdata.name_len &&
1385 bcmp(name, chain->data->ipdata.filename, name_len) == 0) {
1386 break;
1387 }
1388 chain = hammer2_chain_next(hmp, &parent, chain,
1389 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
1390 0);
1391 }
1392
1393 /*
6934ae32 1394 * Not found or wrong type (isdir < 0 disables the type check).
4e2004ea
MD
1395 */
1396 if (chain == NULL) {
1397 hammer2_chain_put(hmp, parent);
1398 return ENOENT;
1399 }
6934ae32
MD
1400 if (chain->data->ipdata.type == HAMMER2_OBJTYPE_DIRECTORY &&
1401 isdir == 0) {
4e2004ea
MD
1402 error = ENOTDIR;
1403 goto done;
1404 }
6934ae32
MD
1405 if (chain->data->ipdata.type != HAMMER2_OBJTYPE_DIRECTORY &&
1406 isdir == 1) {
4e2004ea
MD
1407 error = EISDIR;
1408 goto done;
1409 }
1410
1411 /*
6934ae32
MD
1412 * If this is a directory the directory must be empty. However, if
1413 * isdir < 0 we are doing a rename and the directory does not have
1414 * to be empty.
4e2004ea 1415 */
6934ae32
MD
1416 if (chain->data->ipdata.type == HAMMER2_OBJTYPE_DIRECTORY &&
1417 isdir >= 0) {
4e2004ea
MD
1418 dparent = chain;
1419 hammer2_chain_ref(hmp, dparent);
1420 hammer2_chain_lock(hmp, dparent);
1421 dchain = hammer2_chain_lookup(hmp, &dparent,
1422 0, (hammer2_key_t)-1,
1423 HAMMER2_LOOKUP_NOLOCK);
1424 if (dchain) {
1425 hammer2_chain_drop(hmp, dchain);
1426 hammer2_chain_put(hmp, dparent);
1427 error = ENOTEMPTY;
1428 goto done;
1429 }
1430 hammer2_chain_put(hmp, dparent);
1431 dparent = NULL;
1432 /* dchain NULL */
1433 }
1434
1435 /*
1436 * Found, the chain represents the inode. Remove the parent reference
1437 * to the chain. The chain itself is no longer referenced and will
1438 * be marked unmodified by hammer2_chain_delete(), avoiding unnecessary
1439 * I/O.
1440 */
1441 hammer2_chain_delete(hmp, parent, chain);
1442 /* XXX nlinks (hardlink special case) */
1443 /* XXX nlinks (parent directory) */
1444
6934ae32
MD
1445 if (chain->u.ip->vp) {
1446 vp = hammer2_igetv(chain->u.ip, &error);
1447 if (error == 0) {
1448 vn_unlock(vp);
1449 /* hammer2_knote(vp, NOTE_DELETE); */
1450 cache_inval_vp(vp, CINV_DESTROY);
1451 vrele(vp);
1452 }
4e2004ea
MD
1453 }
1454 error = 0;
1455
1456done:
1457 hammer2_chain_put(hmp, chain);
1458 hammer2_chain_put(hmp, parent);
1459
1460 return error;
1461}
1462
1463
db71f61f
MD
1464static int hammer2_strategy_read(struct vop_strategy_args *ap);
1465static int hammer2_strategy_write(struct vop_strategy_args *ap);
1466
e118c14f 1467static
703720e4 1468int
e118c14f 1469hammer2_vop_strategy(struct vop_strategy_args *ap)
703720e4 1470{
703720e4
MD
1471 struct bio *biop;
1472 struct buf *bp;
703720e4
MD
1473 int error;
1474
703720e4
MD
1475 biop = ap->a_bio;
1476 bp = biop->bio_buf;
703720e4
MD
1477
1478 switch(bp->b_cmd) {
9c2e0de0 1479 case BUF_CMD_READ:
db71f61f
MD
1480 error = hammer2_strategy_read(ap);
1481 break;
9c2e0de0 1482 case BUF_CMD_WRITE:
db71f61f
MD
1483 error = hammer2_strategy_write(ap);
1484 break;
703720e4
MD
1485 default:
1486 bp->b_error = error = EINVAL;
1487 bp->b_flags |= B_ERROR;
1488 biodone(biop);
1489 break;
1490 }
1491
1492 return (error);
1493}
1494
db71f61f
MD
1495static
1496int
1497hammer2_strategy_read(struct vop_strategy_args *ap)
1498{
1499 struct buf *bp;
1500 struct bio *bio;
1501 struct bio *nbio;
1502 hammer2_mount_t *hmp;
1503 hammer2_inode_t *ip;
1504 hammer2_chain_t *parent;
1505 hammer2_chain_t *chain;
5b4a2132
MD
1506 hammer2_key_t loff;
1507 hammer2_off_t poff;
3ac6a319
MD
1508 size_t ddlen = 0; /* direct data shortcut */
1509 char *ddata = NULL;
db71f61f
MD
1510
1511 bio = ap->a_bio;
1512 bp = bio->bio_buf;
1513 ip = VTOI(ap->a_vp);
1514 hmp = ip->hmp;
1515 nbio = push_bio(bio);
1516
1517 if (nbio->bio_offset == NOOFFSET) {
5b4a2132
MD
1518 loff = bio->bio_offset;
1519 KKASSERT((loff & HAMMER2_LBUFMASK64) == 0);
db71f61f
MD
1520
1521 parent = &ip->chain;
1522 hammer2_chain_ref(hmp, parent);
1523 hammer2_chain_lock(hmp, parent);
c667909f
MD
1524
1525 /*
1526 * Specifying NOLOCK avoids unnecessary bread()s of the
1527 * chain element's content. We just need the block device
1528 * offset.
1529 */
5b4a2132 1530 chain = hammer2_chain_lookup(hmp, &parent, loff, loff,
c667909f 1531 HAMMER2_LOOKUP_NOLOCK);
3ac6a319
MD
1532 if (chain == NULL) {
1533 /*
1534 * Data is zero-fill
1535 */
1536 nbio->bio_offset = ZFOFFSET;
1537 } else if (chain->bref.type == HAMMER2_BREF_TYPE_DATA) {
1538 /*
1539 * Data is on-media, implement direct-read
1540 */
5b4a2132
MD
1541 poff = loff - chain->bref.key +
1542 (chain->bref.data_off & HAMMER2_OFF_MASK);
1543 nbio->bio_offset = poff;
c667909f 1544 hammer2_chain_drop(hmp, chain);
3ac6a319
MD
1545 } else if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
1546 /*
1547 * Data is embedded in the inode
1548 */
1549 ddata = chain->data->ipdata.u.data;
1550 ddlen = HAMMER2_EMBEDDED_BYTES;
1551 KKASSERT(chain == parent);
1552 hammer2_chain_drop(hmp, chain);
1553 /* leave bio_offset set to NOOFFSET */
db71f61f 1554 } else {
3ac6a319 1555 panic("hammer2_strategy_read: unknown bref type");
db71f61f
MD
1556 }
1557 hammer2_chain_put(hmp, parent);
1558 }
3ac6a319
MD
1559 if (ddlen) {
1560 /*
1561 * Data embedded directly in inode
1562 */
1563 bp->b_resid = 0;
1564 bp->b_error = 0;
1565 vfs_bio_clrbuf(bp);
1566 bcopy(ddata, bp->b_data, ddlen);
1567 biodone(nbio);
1568 } else if (nbio->bio_offset == ZFOFFSET) {
1569 /*
1570 * Data is zero-fill
1571 */
db71f61f
MD
1572 bp->b_resid = 0;
1573 bp->b_error = 0;
1574 vfs_bio_clrbuf(bp);
1575 biodone(nbio);
1576 } else {
3ac6a319
MD
1577 /*
1578 * Data on media
1579 */
db71f61f
MD
1580 vn_strategy(hmp->devvp, nbio);
1581 }
1582 return (0);
1583}
1584
1585static
1586int
1587hammer2_strategy_write(struct vop_strategy_args *ap)
1588{
1589 struct buf *bp;
1590 struct bio *bio;
1591 struct bio *nbio;
1592 hammer2_mount_t *hmp;
1593 hammer2_inode_t *ip;
1594 hammer2_chain_t *parent;
1595 hammer2_chain_t *chain;
1596 hammer2_key_t off_hi;
1597 int off_lo;
1598
1599 bio = ap->a_bio;
1600 bp = bio->bio_buf;
1601 ip = VTOI(ap->a_vp);
1602 hmp = ip->hmp;
1603 nbio = push_bio(bio);
1604
1605 /*
1606 * Our bmap doesn't support writes atm, and a vop_write should
1607 * clear the physical disk offset cache for the copy-on-write
1608 * operation.
1609 */
1610 KKASSERT(nbio->bio_offset == NOOFFSET);
1611
1612 off_hi = bio->bio_offset & HAMMER2_OFF_MASK_HI;
1613 off_lo = bio->bio_offset & HAMMER2_OFF_MASK_LO;
1614 KKASSERT((bio->bio_offset & HAMMER2_LBUFMASK64) == 0);
1615
1616 parent = &ip->chain;
1617 hammer2_chain_ref(hmp, parent);
1618 hammer2_chain_lock(hmp, parent);
3ac6a319
MD
1619 /*
1620 * XXX implement NODATA flag to avoid instantiating bp if
1621 * it isn't already present for direct-write implementation.
1622 */
c667909f 1623 chain = hammer2_chain_lookup(hmp, &parent, off_hi, off_hi, 0);
3ac6a319
MD
1624 if (chain == NULL) {
1625 /*
1626 * A new data block must be allocated.
1627 */
6934ae32 1628 chain = hammer2_chain_create(hmp, parent, NULL,
db71f61f
MD
1629 off_hi, HAMMER2_PBUFRADIX,
1630 HAMMER2_BREF_TYPE_DATA,
1631 HAMMER2_PBUFSIZE);
1632 bcopy(bp->b_data, chain->data->buf + off_lo, bp->b_bcount);
3ac6a319
MD
1633 } else if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
1634 /*
1635 * The data is embedded in the inode
1636 */
1637 hammer2_chain_modify(hmp, chain);
1638 if (off_lo < HAMMER2_EMBEDDED_BYTES) {
1639 bcopy(bp->b_data,
1640 chain->data->ipdata.u.data + off_lo,
1641 HAMMER2_EMBEDDED_BYTES - off_lo);
1642 }
1643 } else {
1644 /*
1645 * The data is on media, possibly in a larger block.
1646 *
1647 * XXX implement direct-write if bp not cached using NODATA
1648 * flag.
1649 */
1650 hammer2_chain_modify(hmp, chain);
1651 KKASSERT(bp->b_bcount <= HAMMER2_PBUFSIZE - off_lo);
1652 bcopy(bp->b_data, chain->data->buf + off_lo, bp->b_bcount);
db71f61f 1653 }
37aa19df
MD
1654 if (off_lo + bp->b_bcount == HAMMER2_PBUFSIZE)
1655 atomic_set_int(&chain->flags, HAMMER2_CHAIN_IOFLUSH);
1656 hammer2_chain_put(hmp, chain);
db71f61f
MD
1657 hammer2_chain_put(hmp, parent);
1658
1659 bp->b_resid = 0;
1660 bp->b_error = 0;
1661 biodone(nbio);
1662
1663 return (0);
1664}
1665
e118c14f 1666static
f0206a67 1667int
e118c14f 1668hammer2_vop_mountctl(struct vop_mountctl_args *ap)
f0206a67
VS
1669{
1670 struct mount *mp;
1671 struct hammer2_mount *hmp;
1672 int rc;
1673
1674 switch (ap->a_op) {
1675 case (MOUNTCTL_SET_EXPORT):
1676 mp = ap->a_head.a_ops->head.vv_mount;
1677 hmp = MPTOH2(mp);
1678
1679 if (ap->a_ctllen != sizeof(struct export_args))
1680 rc = (EINVAL);
1681 else
10c5dee0
MD
1682 rc = vfs_export(mp, &hmp->export,
1683 (const struct export_args *)ap->a_ctl);
f0206a67
VS
1684 break;
1685 default:
1686 rc = vop_stdmountctl(ap);
1687 break;
1688 }
1689 return (rc);
1690}
1691
703720e4
MD
1692struct vop_ops hammer2_vnode_vops = {
1693 .vop_default = vop_defaultop,
e118c14f 1694 .vop_fsync = hammer2_vop_fsync,
703720e4
MD
1695 .vop_getpages = vop_stdgetpages,
1696 .vop_putpages = vop_stdputpages,
e118c14f 1697 .vop_access = hammer2_vop_access,
37aa19df 1698 .vop_advlock = hammer2_vop_advlock,
c667909f
MD
1699 .vop_close = hammer2_vop_close,
1700 .vop_ncreate = hammer2_vop_ncreate,
4e2004ea
MD
1701 .vop_nsymlink = hammer2_vop_nsymlink,
1702 .vop_nremove = hammer2_vop_nremove,
1703 .vop_nrmdir = hammer2_vop_nrmdir,
1704 .vop_nrename = hammer2_vop_nrename,
e118c14f 1705 .vop_getattr = hammer2_vop_getattr,
3ac6a319 1706 .vop_setattr = hammer2_vop_setattr,
e118c14f 1707 .vop_readdir = hammer2_vop_readdir,
4e2004ea 1708 .vop_readlink = hammer2_vop_readlink,
5b4a2132
MD
1709 .vop_getpages = vop_stdgetpages,
1710 .vop_putpages = vop_stdputpages,
e118c14f
MD
1711 .vop_read = hammer2_vop_read,
1712 .vop_write = hammer2_vop_write,
1713 .vop_open = hammer2_vop_open,
1714 .vop_inactive = hammer2_vop_inactive,
1715 .vop_reclaim = hammer2_vop_reclaim,
1716 .vop_nresolve = hammer2_vop_nresolve,
37494cab
MD
1717 .vop_nlookupdotdot = hammer2_vop_nlookupdotdot,
1718 .vop_nmkdir = hammer2_vop_nmkdir,
e118c14f
MD
1719 .vop_mountctl = hammer2_vop_mountctl,
1720 .vop_bmap = hammer2_vop_bmap,
1721 .vop_strategy = hammer2_vop_strategy,
703720e4
MD
1722};
1723
1724struct vop_ops hammer2_spec_vops = {
1725
1726};
1727
1728struct vop_ops hammer2_fifo_vops = {
1729
1730};