usr.sbin/makefs: Sync with sys/vfs/hammer2
[dragonfly.git] / usr.sbin / makefs / hammer2 / hammer2_vnops.c
blob104f145e3648a59b155391f0549334d529aef312
1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved.
7 * This code is derived from software contributed to The DragonFly Project
8 * by Matthew Dillon <dillon@dragonflybsd.org>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of The DragonFly Project nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific, prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 * Kernel Filesystem interface
40 * NOTE! local ipdata pointers must be reloaded on any modifying operation
41 * to the inode as its underlying chain may have changed.
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/fcntl.h>
49 #include <sys/buf.h>
50 #include <sys/proc.h>
51 #include <sys/mount.h>
52 #include <sys/vnode.h>
53 #include <sys/mountctl.h>
55 #include <sys/dirent.h>
57 #include <sys/uio.h>
58 #include <sys/objcache.h>
59 #include <sys/event.h>
60 #include <sys/file.h>
61 #include <vfs/fifofs/fifo.h>
64 #include "hammer2.h"
66 static int hammer2_read_file(hammer2_inode_t *ip, struct uio *uio,
67 int seqcount);
68 static int hammer2_write_file(hammer2_inode_t *ip, struct uio *uio,
69 int ioflag, int seqcount);
70 static void hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize);
71 static void hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize);
74 * Last reference to a vnode is going away but it is still cached.
76 static
77 int
78 hammer2_vop_inactive(struct vop_inactive_args *ap)
80 #if 0
81 hammer2_inode_t *ip;
82 struct m_vnode *vp;
84 vp = ap->a_vp;
85 ip = VTOI(vp);
88 * Degenerate case
90 if (ip == NULL) {
91 vrecycle(vp);
92 return (0);
96 * Aquire the inode lock to interlock against vp updates via
97 * the inode path and file deletions and such (which can be
98 * namespace-only operations that might not hold the vnode).
100 hammer2_inode_lock(ip, 0);
101 if (ip->flags & HAMMER2_INODE_ISUNLINKED) {
102 int nblksize;
105 * If the inode has been unlinked we can throw away all
106 * buffers (dirty or not) and clean the file out.
108 * Because vrecycle() calls are not guaranteed, try to
109 * dispose of the inode as much as possible right here.
111 nblksize = hammer2_calc_logical(ip, 0, NULL, NULL);
112 nvtruncbuf(vp, 0, nblksize, 0, 0);
115 * Delete the file on-media.
117 if ((ip->flags & HAMMER2_INODE_DELETING) == 0) {
118 atomic_set_int(&ip->flags, HAMMER2_INODE_DELETING);
119 hammer2_inode_delayed_sideq(ip);
121 hammer2_inode_unlock(ip);
124 * Recycle immediately if possible
126 vrecycle(vp);
127 } else {
128 hammer2_inode_unlock(ip);
130 return (0);
131 #endif
132 return (EOPNOTSUPP);
136 * Reclaim a vnode so that it can be reused; after the inode is
137 * disassociated, the filesystem must manage it alone.
139 static
141 hammer2_vop_reclaim(struct vop_reclaim_args *ap)
143 hammer2_inode_t *ip;
144 struct m_vnode *vp;
146 vp = ap->a_vp;
147 ip = VTOI(vp);
148 if (ip == NULL)
149 return(0);
152 * NOTE! We do not attempt to flush chains here, flushing is
153 * really fragile and could also deadlock.
155 vclrisdirty(vp);
158 * The inode lock is required to disconnect it.
160 hammer2_inode_lock(ip, 0);
161 vp->v_data = NULL;
162 ip->vp = NULL;
165 * Delete the file on-media. This should have been handled by the
166 * inactivation. The operation is likely still queued on the inode
167 * though so only complain if the stars don't align.
169 if ((ip->flags & (HAMMER2_INODE_ISUNLINKED | HAMMER2_INODE_DELETING)) ==
170 HAMMER2_INODE_ISUNLINKED)
172 assert(0);
173 atomic_set_int(&ip->flags, HAMMER2_INODE_DELETING);
174 hammer2_inode_delayed_sideq(ip);
175 kprintf("hammer2: vp=%p ip=%p unlinked but not disposed\n",
176 vp, ip);
178 hammer2_inode_unlock(ip);
181 * Modified inodes will already be on SIDEQ or SYNCQ, no further
182 * action is needed.
184 * We cannot safely synchronize the inode from inside the reclaim
185 * due to potentially deep locks held as-of when the reclaim occurs.
186 * Interactions and potential deadlocks abound. We also can't do it
187 * here without desynchronizing from the related directory entrie(s).
189 hammer2_inode_drop(ip); /* vp ref */
192 * XXX handle background sync when ip dirty, kernel will no longer
193 * notify us regarding this inode because there is no longer a
194 * vnode attached to it.
197 return (0);
201 hammer2_reclaim(struct m_vnode *vp)
203 struct vop_reclaim_args ap = {
204 .a_vp = vp,
207 return hammer2_vop_reclaim(&ap);
211 * Currently this function synchronizes the front-end inode state to the
212 * backend chain topology, then flushes the inode's chain and sub-topology
213 * to backend media. This function does not flush the root topology down to
214 * the inode.
216 static
218 hammer2_vop_fsync(struct vop_fsync_args *ap)
220 #if 0
221 hammer2_inode_t *ip;
222 struct m_vnode *vp;
223 int error1;
224 int error2;
226 vp = ap->a_vp;
227 ip = VTOI(vp);
228 error1 = 0;
230 hammer2_trans_init(ip->pmp, 0);
233 * Flush dirty buffers in the file's logical buffer cache.
234 * It is best to wait for the strategy code to commit the
235 * buffers to the device's backing buffer cache before
236 * then trying to flush the inode.
238 * This should be quick, but certain inode modifications cached
239 * entirely in the hammer2_inode structure may not trigger a
240 * buffer read until the flush so the fsync can wind up also
241 * doing scattered reads.
243 vfsync(vp, ap->a_waitfor, 1, NULL, NULL);
244 bio_track_wait(&vp->v_track_write, 0, 0);
247 * Flush any inode changes
249 hammer2_inode_lock(ip, 0);
250 if (ip->flags & (HAMMER2_INODE_RESIZED|HAMMER2_INODE_MODIFIED))
251 error1 = hammer2_inode_chain_sync(ip);
254 * Flush dirty chains related to the inode.
256 * NOTE! We are not in a flush transaction. The inode remains on
257 * the sideq so the filesystem syncer can synchronize it to
258 * the volume root.
260 error2 = hammer2_inode_chain_flush(ip, HAMMER2_XOP_INODE_STOP);
261 if (error2)
262 error1 = error2;
265 * We may be able to clear the vnode dirty flag.
267 if ((ip->flags & (HAMMER2_INODE_MODIFIED |
268 HAMMER2_INODE_RESIZED |
269 HAMMER2_INODE_DIRTYDATA)) == 0 &&
270 RB_EMPTY(&vp->v_rbdirty_tree) &&
271 !bio_track_active(&vp->v_track_write)) {
272 vclrisdirty(vp);
274 hammer2_inode_unlock(ip);
275 hammer2_trans_done(ip->pmp, 0);
277 return (error1);
278 #endif
279 return (EOPNOTSUPP);
283 * No lock needed, just handle ip->update
285 static
287 hammer2_vop_access(struct vop_access_args *ap)
289 #if 0
290 hammer2_inode_t *ip = VTOI(ap->a_vp);
291 uid_t uid;
292 gid_t gid;
293 mode_t mode;
294 uint32_t uflags;
295 int error;
296 int update;
298 retry:
299 update = spin_access_start(&ip->cluster_spin);
301 /*hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);*/
302 uid = hammer2_to_unix_xid(&ip->meta.uid);
303 gid = hammer2_to_unix_xid(&ip->meta.gid);
304 mode = ip->meta.mode;
305 uflags = ip->meta.uflags;
306 /*hammer2_inode_unlock(ip);*/
308 if (__predict_false(spin_access_end(&ip->cluster_spin, update)))
309 goto retry;
311 error = vop_helper_access(ap, uid, gid, mode, uflags);
313 return (error);
314 #endif
315 return (EOPNOTSUPP);
318 static
320 hammer2_vop_getattr(struct vop_getattr_args *ap)
322 #if 0
323 hammer2_pfs_t *pmp;
324 hammer2_inode_t *ip;
325 struct m_vnode *vp;
326 struct vattr *vap;
327 int update;
329 vp = ap->a_vp;
330 vap = ap->a_vap;
332 ip = VTOI(vp);
333 pmp = ip->pmp;
335 retry:
336 update = spin_access_start(&ip->cluster_spin);
338 vap->va_fsid = pmp->mp->mnt_stat.f_fsid.val[0];
339 vap->va_fileid = ip->meta.inum;
340 vap->va_mode = ip->meta.mode;
341 vap->va_nlink = ip->meta.nlinks;
342 vap->va_uid = hammer2_to_unix_xid(&ip->meta.uid);
343 vap->va_gid = hammer2_to_unix_xid(&ip->meta.gid);
344 vap->va_rmajor = 0;
345 vap->va_rminor = 0;
346 vap->va_size = ip->meta.size; /* protected by shared lock */
347 vap->va_blocksize = HAMMER2_PBUFSIZE;
348 vap->va_flags = ip->meta.uflags;
349 hammer2_time_to_timespec(ip->meta.ctime, &vap->va_ctime);
350 hammer2_time_to_timespec(ip->meta.mtime, &vap->va_mtime);
351 hammer2_time_to_timespec(ip->meta.mtime, &vap->va_atime);
352 vap->va_gen = 1;
353 vap->va_bytes = 0;
354 if (ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY) {
356 * Can't really calculate directory use sans the files under
357 * it, just assume one block for now.
359 vap->va_bytes += HAMMER2_INODE_BYTES;
360 } else {
361 vap->va_bytes = hammer2_inode_data_count(ip);
363 vap->va_type = hammer2_get_vtype(ip->meta.type);
364 vap->va_filerev = 0;
365 vap->va_uid_uuid = ip->meta.uid;
366 vap->va_gid_uuid = ip->meta.gid;
367 vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
368 VA_FSID_UUID_VALID;
370 if (__predict_false(spin_access_end(&ip->cluster_spin, update)))
371 goto retry;
373 return (0);
374 #endif
375 return (EOPNOTSUPP);
378 static
380 hammer2_vop_getattr_lite(struct vop_getattr_lite_args *ap)
382 #if 0
383 hammer2_pfs_t *pmp;
384 hammer2_inode_t *ip;
385 struct m_vnode *vp;
386 struct vattr_lite *lvap;
387 int update;
389 vp = ap->a_vp;
390 lvap = ap->a_lvap;
392 ip = VTOI(vp);
393 pmp = ip->pmp;
395 retry:
396 update = spin_access_start(&ip->cluster_spin);
398 #if 0
399 vap->va_fsid = pmp->mp->mnt_stat.f_fsid.val[0];
400 vap->va_fileid = ip->meta.inum;
401 #endif
402 lvap->va_mode = ip->meta.mode;
403 lvap->va_nlink = ip->meta.nlinks;
404 lvap->va_uid = hammer2_to_unix_xid(&ip->meta.uid);
405 lvap->va_gid = hammer2_to_unix_xid(&ip->meta.gid);
406 #if 0
407 vap->va_rmajor = 0;
408 vap->va_rminor = 0;
409 #endif
410 lvap->va_size = ip->meta.size;
411 #if 0
412 vap->va_blocksize = HAMMER2_PBUFSIZE;
413 #endif
414 lvap->va_flags = ip->meta.uflags;
415 lvap->va_type = hammer2_get_vtype(ip->meta.type);
416 #if 0
417 vap->va_filerev = 0;
418 vap->va_uid_uuid = ip->meta.uid;
419 vap->va_gid_uuid = ip->meta.gid;
420 vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
421 VA_FSID_UUID_VALID;
422 #endif
424 if (__predict_false(spin_access_end(&ip->cluster_spin, update)))
425 goto retry;
427 return (0);
428 #endif
429 return (EOPNOTSUPP);
432 static
434 hammer2_vop_setattr(struct vop_setattr_args *ap)
436 #if 0
437 hammer2_inode_t *ip;
438 struct m_vnode *vp;
439 struct vattr *vap;
440 int error;
441 int kflags = 0;
442 uint64_t ctime;
444 vp = ap->a_vp;
445 vap = ap->a_vap;
446 hammer2_update_time(&ctime);
448 ip = VTOI(vp);
450 if (ip->pmp->ronly)
451 return (EROFS);
454 * Normally disallow setattr if there is no space, unless we
455 * are in emergency mode (might be needed to chflags -R noschg
456 * files prior to removal).
458 if ((ip->pmp->flags & HAMMER2_PMPF_EMERG) == 0 &&
459 hammer2_vfs_enospace(ip, 0, ap->a_cred) > 1) {
460 return (ENOSPC);
463 hammer2_trans_init(ip->pmp, 0);
464 hammer2_inode_lock(ip, 0);
465 error = 0;
467 if (vap->va_flags != VNOVAL) {
468 uint32_t flags;
470 flags = ip->meta.uflags;
471 error = vop_helper_setattr_flags(&flags, vap->va_flags,
472 hammer2_to_unix_xid(&ip->meta.uid),
473 ap->a_cred);
474 if (error == 0) {
475 if (ip->meta.uflags != flags) {
476 hammer2_inode_modify(ip);
477 hammer2_spin_lock_update(&ip->cluster_spin);
478 ip->meta.uflags = flags;
479 ip->meta.ctime = ctime;
480 hammer2_spin_unlock_update(&ip->cluster_spin);
481 kflags |= NOTE_ATTRIB;
483 if (ip->meta.uflags & (IMMUTABLE | APPEND)) {
484 error = 0;
485 goto done;
488 goto done;
490 if (ip->meta.uflags & (IMMUTABLE | APPEND)) {
491 error = EPERM;
492 goto done;
494 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
495 mode_t cur_mode = ip->meta.mode;
496 uid_t cur_uid = hammer2_to_unix_xid(&ip->meta.uid);
497 gid_t cur_gid = hammer2_to_unix_xid(&ip->meta.gid);
498 uuid_t uuid_uid;
499 uuid_t uuid_gid;
501 error = vop_helper_chown(ap->a_vp, vap->va_uid, vap->va_gid,
502 ap->a_cred,
503 &cur_uid, &cur_gid, &cur_mode);
504 if (error == 0) {
505 hammer2_guid_to_uuid(&uuid_uid, cur_uid);
506 hammer2_guid_to_uuid(&uuid_gid, cur_gid);
507 if (bcmp(&uuid_uid, &ip->meta.uid, sizeof(uuid_uid)) ||
508 bcmp(&uuid_gid, &ip->meta.gid, sizeof(uuid_gid)) ||
509 ip->meta.mode != cur_mode
511 hammer2_inode_modify(ip);
512 hammer2_spin_lock_update(&ip->cluster_spin);
513 ip->meta.uid = uuid_uid;
514 ip->meta.gid = uuid_gid;
515 ip->meta.mode = cur_mode;
516 ip->meta.ctime = ctime;
517 hammer2_spin_unlock_update(&ip->cluster_spin);
519 kflags |= NOTE_ATTRIB;
524 * Resize the file
526 if (vap->va_size != VNOVAL && ip->meta.size != vap->va_size) {
527 switch(vp->v_type) {
528 case VREG:
529 if (vap->va_size == ip->meta.size)
530 break;
531 if (vap->va_size < ip->meta.size) {
532 hammer2_mtx_ex(&ip->truncate_lock);
533 hammer2_truncate_file(ip, vap->va_size);
534 hammer2_mtx_unlock(&ip->truncate_lock);
535 kflags |= NOTE_WRITE;
536 } else {
537 hammer2_extend_file(ip, vap->va_size);
538 kflags |= NOTE_WRITE | NOTE_EXTEND;
540 hammer2_inode_modify(ip);
541 ip->meta.mtime = ctime;
542 vclrflags(vp, VLASTWRITETS);
543 break;
544 default:
545 error = EINVAL;
546 goto done;
549 #if 0
550 /* atime not supported */
551 if (vap->va_atime.tv_sec != VNOVAL) {
552 hammer2_inode_modify(ip);
553 ip->meta.atime = hammer2_timespec_to_time(&vap->va_atime);
554 kflags |= NOTE_ATTRIB;
556 #endif
557 if (vap->va_mode != (mode_t)VNOVAL) {
558 mode_t cur_mode = ip->meta.mode;
559 uid_t cur_uid = hammer2_to_unix_xid(&ip->meta.uid);
560 gid_t cur_gid = hammer2_to_unix_xid(&ip->meta.gid);
562 error = vop_helper_chmod(ap->a_vp, vap->va_mode, ap->a_cred,
563 cur_uid, cur_gid, &cur_mode);
564 if (error == 0) {
565 hammer2_inode_modify(ip);
566 hammer2_spin_lock_update(&ip->cluster_spin);
567 ip->meta.mode = cur_mode;
568 ip->meta.ctime = ctime;
569 hammer2_spin_unlock_update(&ip->cluster_spin);
570 kflags |= NOTE_ATTRIB;
574 if (vap->va_mtime.tv_sec != VNOVAL) {
575 hammer2_inode_modify(ip);
576 ip->meta.mtime = hammer2_timespec_to_time(&vap->va_mtime);
577 kflags |= NOTE_ATTRIB;
578 vclrflags(vp, VLASTWRITETS);
581 done:
583 * If a truncation occurred we must call chain_sync() now in order
584 * to trim the related data chains, otherwise a later expansion can
585 * cause havoc.
587 * If an extend occured that changed the DIRECTDATA state, we must
588 * call inode_chain_sync now in order to prepare the inode's indirect
589 * block table.
591 * WARNING! This means we are making an adjustment to the inode's
592 * chain outside of sync/fsync, and not just to inode->meta, which
593 * may result in some consistency issues if a crash were to occur
594 * at just the wrong time.
596 if (ip->flags & HAMMER2_INODE_RESIZED)
597 hammer2_inode_chain_sync(ip);
600 * Cleanup.
602 hammer2_inode_unlock(ip);
603 hammer2_trans_done(ip->pmp, HAMMER2_TRANS_SIDEQ);
604 hammer2_knote(ip->vp, kflags);
606 return (error);
607 #endif
608 return (EOPNOTSUPP);
611 static int
612 vop_write_dirent(int *error, struct uio *uio, ino_t d_ino, uint8_t d_type,
613 uint16_t d_namlen, const char *d_name)
615 struct dirent *dp;
616 size_t len;
618 len = _DIRENT_RECLEN(d_namlen);
619 if (len > uio->uio_resid)
620 return(1);
622 dp = kmalloc(len, M_TEMP, M_WAITOK | M_ZERO);
624 dp->d_ino = d_ino;
625 dp->d_namlen = d_namlen;
626 dp->d_type = d_type;
627 bcopy(d_name, dp->d_name, d_namlen);
629 *error = uiomove((caddr_t)dp, len, uio);
631 kfree(dp, M_TEMP);
633 return(0);
636 static
638 hammer2_vop_readdir(struct vop_readdir_args *ap)
640 hammer2_xop_readdir_t *xop;
641 hammer2_blockref_t bref;
642 hammer2_inode_t *ip;
643 hammer2_tid_t inum;
644 hammer2_key_t lkey;
645 struct uio *uio;
646 off_t *cookies;
647 off_t saveoff;
648 int cookie_index;
649 int ncookies;
650 int error;
651 int ndirent;
652 int eofflag;
653 int r;
655 ip = VTOI(ap->a_vp);
656 uio = ap->a_uio;
657 saveoff = uio->uio_offset;
658 ndirent = 0;
659 eofflag = 0;
660 error = 0;
663 * Setup cookies directory entry cookies if requested
665 if (ap->a_ncookies) {
666 ncookies = uio->uio_resid / 16 + 1;
667 if (ncookies > 1024)
668 ncookies = 1024;
669 cookies = kmalloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK);
670 } else {
671 ncookies = -1;
672 cookies = NULL;
674 cookie_index = 0;
676 hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
679 * Handle artificial entries. To ensure that only positive 64 bit
680 * quantities are returned to userland we always strip off bit 63.
681 * The hash code is designed such that codes 0x0000-0x7FFF are not
682 * used, allowing us to use these codes for articial entries.
684 * Entry 0 is used for '.' and entry 1 is used for '..'. Do not
685 * allow '..' to cross the mount point into (e.g.) the super-root.
687 if (saveoff == 0) {
688 inum = ip->meta.inum & HAMMER2_DIRHASH_USERMSK;
689 r = vop_write_dirent(&error, uio, inum, DT_DIR, 1, ".");
690 if (r)
691 goto done;
692 if (cookies)
693 cookies[cookie_index] = saveoff;
694 ++saveoff;
695 ++cookie_index;
696 ++ndirent;
697 if (cookie_index == ncookies)
698 goto done;
701 if (saveoff == 1) {
702 inum = ip->meta.inum & HAMMER2_DIRHASH_USERMSK;
703 if (ip != ip->pmp->iroot)
704 inum = ip->meta.iparent & HAMMER2_DIRHASH_USERMSK;
705 r = vop_write_dirent(&error, uio, inum, DT_DIR, 2, "..");
706 if (r)
707 goto done;
708 if (cookies)
709 cookies[cookie_index] = saveoff;
710 ++saveoff;
711 ++cookie_index;
712 ++ndirent;
713 if (cookie_index == ncookies)
714 goto done;
717 lkey = saveoff | HAMMER2_DIRHASH_VISIBLE;
718 if (hammer2_debug & 0x0020)
719 kprintf("readdir: lkey %016jx\n", lkey);
720 if (error)
721 goto done;
723 xop = hammer2_xop_alloc(ip, 0);
724 xop->lkey = lkey;
725 hammer2_xop_start(&xop->head, &hammer2_readdir_desc);
727 for (;;) {
728 const hammer2_inode_data_t *ripdata;
729 const char *dname;
730 int dtype;
732 error = hammer2_xop_collect(&xop->head, 0);
733 error = hammer2_error_to_errno(error);
734 if (error) {
735 break;
737 if (cookie_index == ncookies)
738 break;
739 if (hammer2_debug & 0x0020)
740 kprintf("cluster chain %p %p\n",
741 xop->head.cluster.focus,
742 (xop->head.cluster.focus ?
743 xop->head.cluster.focus->data : (void *)-1));
744 hammer2_cluster_bref(&xop->head.cluster, &bref);
746 if (bref.type == HAMMER2_BREF_TYPE_INODE) {
747 ripdata = &hammer2_xop_gdata(&xop->head)->ipdata;
748 dtype = hammer2_get_dtype(ripdata->meta.type);
749 saveoff = bref.key & HAMMER2_DIRHASH_USERMSK;
750 r = vop_write_dirent(&error, uio,
751 ripdata->meta.inum &
752 HAMMER2_DIRHASH_USERMSK,
753 dtype,
754 ripdata->meta.name_len,
755 ripdata->filename);
756 hammer2_xop_pdata(&xop->head);
757 if (r)
758 break;
759 if (cookies)
760 cookies[cookie_index] = saveoff;
761 ++cookie_index;
762 ++ndirent;
763 } else if (bref.type == HAMMER2_BREF_TYPE_DIRENT) {
764 uint16_t namlen;
766 dtype = hammer2_get_dtype(bref.embed.dirent.type);
767 saveoff = bref.key & HAMMER2_DIRHASH_USERMSK;
768 namlen = bref.embed.dirent.namlen;
769 if (namlen <= sizeof(bref.check.buf)) {
770 dname = bref.check.buf;
771 } else {
772 dname = hammer2_xop_gdata(&xop->head)->buf;
774 r = vop_write_dirent(&error, uio,
775 bref.embed.dirent.inum, dtype,
776 namlen, dname);
777 if (namlen > sizeof(bref.check.buf))
778 hammer2_xop_pdata(&xop->head);
779 if (r)
780 break;
781 if (cookies)
782 cookies[cookie_index] = saveoff;
783 ++cookie_index;
784 ++ndirent;
785 } else {
786 /* XXX chain error */
787 kprintf("bad chain type readdir %d\n", bref.type);
790 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
791 if (error == ENOENT) {
792 error = 0;
793 eofflag = 1;
794 saveoff = (hammer2_key_t)-1;
795 } else {
796 saveoff = bref.key & HAMMER2_DIRHASH_USERMSK;
798 done:
799 hammer2_inode_unlock(ip);
800 if (ap->a_eofflag)
801 *ap->a_eofflag = eofflag;
802 if (hammer2_debug & 0x0020)
803 kprintf("readdir: done at %016jx\n", saveoff);
804 uio->uio_offset = saveoff & ~HAMMER2_DIRHASH_VISIBLE;
805 if (error && cookie_index == 0) {
806 if (cookies) {
807 kfree(cookies, M_TEMP);
808 *ap->a_ncookies = 0;
809 *ap->a_cookies = NULL;
811 } else {
812 if (cookies) {
813 *ap->a_ncookies = cookie_index;
814 *ap->a_cookies = cookies;
817 *ap->a_ndirent = ndirent;
819 return (error);
823 hammer2_readdir(struct m_vnode *vp, void *buf, size_t size, off_t *offsetp,
824 int *ndirentp, int *eofflagp)
826 int error;
828 assert(buf);
829 assert(size > 0);
830 assert(size <= HAMMER2_PBUFSIZE);
832 struct iovec iov = {
833 .iov_base = buf,
834 .iov_len = size,
836 struct uio uio = {
837 .uio_iov = &iov,
838 .uio_iovcnt = 1,
839 .uio_offset = *offsetp,
840 .uio_resid = size,
841 .uio_segflg = UIO_USERSPACE,
842 .uio_rw = UIO_READ,
843 .uio_td = NULL,
845 struct vop_readdir_args ap = {
846 .a_vp = vp,
847 .a_uio = &uio,
848 .a_cred = NULL,
849 .a_eofflag = eofflagp,
850 .a_ncookies = NULL,
851 .a_cookies = NULL,
852 .a_ndirent = ndirentp,
855 error = hammer2_vop_readdir(&ap);
856 *offsetp = uio.uio_offset;
858 return (error);
862 * hammer2_vop_readlink { vp, uio, cred }
864 static
866 hammer2_vop_readlink(struct vop_readlink_args *ap)
868 struct m_vnode *vp;
869 hammer2_inode_t *ip;
870 int error;
872 vp = ap->a_vp;
873 if (vp->v_type != VLNK)
874 return (EINVAL);
875 ip = VTOI(vp);
877 error = hammer2_read_file(ip, ap->a_uio, 0);
878 return (error);
882 hammer2_readlink(struct m_vnode *vp, void *buf, size_t size)
884 assert(buf);
885 assert(size > 0);
886 assert(size <= HAMMER2_PBUFSIZE);
888 struct iovec iov = {
889 .iov_base = buf,
890 .iov_len = size,
892 struct uio uio = {
893 .uio_iov = &iov,
894 .uio_iovcnt = 1,
895 .uio_offset = 0,
896 .uio_resid = size,
897 .uio_segflg = UIO_USERSPACE,
898 .uio_rw = UIO_READ,
899 .uio_td = NULL,
901 struct vop_readlink_args ap = {
902 .a_vp = vp,
903 .a_uio = &uio,
904 .a_cred = NULL,
907 return hammer2_vop_readlink(&ap);
910 static
912 hammer2_vop_read(struct vop_read_args *ap)
914 struct m_vnode *vp;
915 hammer2_inode_t *ip;
916 struct uio *uio;
917 int error;
918 int seqcount;
921 * Read operations supported on this vnode?
923 vp = ap->a_vp;
924 if (vp->v_type == VDIR)
925 return (EISDIR);
926 if (vp->v_type != VREG)
927 return (EINVAL);
930 * Misc
932 ip = VTOI(vp);
933 uio = ap->a_uio;
934 error = 0;
936 seqcount = ap->a_ioflag >> IO_SEQSHIFT;
938 error = hammer2_read_file(ip, uio, seqcount);
939 return (error);
943 hammer2_read(struct m_vnode *vp, void *buf, size_t size, off_t offset)
945 assert(buf);
946 assert(size > 0);
947 assert(size <= HAMMER2_PBUFSIZE);
949 struct iovec iov = {
950 .iov_base = buf,
951 .iov_len = size,
953 struct uio uio = {
954 .uio_iov = &iov,
955 .uio_iovcnt = 1,
956 .uio_offset = offset,
957 .uio_resid = size,
958 .uio_segflg = UIO_USERSPACE,
959 .uio_rw = UIO_READ,
960 .uio_td = NULL,
962 struct vop_read_args ap = {
963 .a_vp = vp,
964 .a_uio = &uio,
965 .a_ioflag = 0,
966 .a_cred = NULL,
969 return hammer2_vop_read(&ap);
972 static
974 hammer2_vop_write(struct vop_write_args *ap)
976 hammer2_inode_t *ip;
977 //thread_t td;
978 struct m_vnode *vp;
979 struct uio *uio;
980 int error;
981 int seqcount;
982 int ioflag;
985 * Read operations supported on this vnode?
987 vp = ap->a_vp;
988 if (vp->v_type != VREG)
989 return (EINVAL);
992 * Misc
994 ip = VTOI(vp);
995 ioflag = ap->a_ioflag;
996 uio = ap->a_uio;
997 error = 0;
998 if (ip->pmp->ronly || (ip->pmp->flags & HAMMER2_PMPF_EMERG))
999 return (EROFS);
1000 switch (hammer2_vfs_enospace(ip, uio->uio_resid, ap->a_cred)) {
1001 case 2:
1002 return (ENOSPC);
1003 case 1:
1004 ioflag |= IO_DIRECT; /* semi-synchronous */
1005 /* fall through */
1006 default:
1007 break;
1010 seqcount = ioflag >> IO_SEQSHIFT;
1013 * Check resource limit
1016 if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc &&
1017 uio->uio_offset + uio->uio_resid >
1018 td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
1019 lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ);
1020 return (EFBIG);
1025 * The transaction interlocks against flush initiations
1026 * (note: but will run concurrently with the actual flush).
1028 * To avoid deadlocking against the VM system, we must flag any
1029 * transaction related to the buffer cache or other direct
1030 * VM page manipulation.
1032 if (uio->uio_segflg == UIO_NOCOPY) {
1033 assert(0); /* no UIO_NOCOPY in makefs */
1034 hammer2_trans_init(ip->pmp, HAMMER2_TRANS_BUFCACHE);
1035 } else {
1036 hammer2_trans_init(ip->pmp, 0);
1038 error = hammer2_write_file(ip, uio, ioflag, seqcount);
1039 if (uio->uio_segflg == UIO_NOCOPY) {
1040 assert(0); /* no UIO_NOCOPY in makefs */
1041 hammer2_trans_done(ip->pmp, HAMMER2_TRANS_BUFCACHE |
1042 HAMMER2_TRANS_SIDEQ);
1043 } else
1044 hammer2_trans_done(ip->pmp, HAMMER2_TRANS_SIDEQ);
1046 return (error);
1050 hammer2_write(struct m_vnode *vp, void *buf, size_t size, off_t offset)
1052 assert(buf);
1053 assert(size > 0);
1054 assert(size <= HAMMER2_PBUFSIZE);
1056 struct iovec iov = {
1057 .iov_base = buf,
1058 .iov_len = size,
1060 struct uio uio = {
1061 .uio_iov = &iov,
1062 .uio_iovcnt = 1,
1063 .uio_offset = offset,
1064 .uio_resid = size,
1065 .uio_segflg = UIO_USERSPACE,
1066 .uio_rw = UIO_WRITE,
1067 .uio_td = NULL,
1069 struct vop_write_args ap = {
1070 .a_vp = vp,
1071 .a_uio = &uio,
1072 .a_ioflag = 0,
1073 .a_cred = NULL,
1076 return hammer2_vop_write(&ap);
1080 * Perform read operations on a file or symlink given an UNLOCKED
1081 * inode and uio.
1083 * The passed ip is not locked.
1085 static
1087 hammer2_read_file(hammer2_inode_t *ip, struct uio *uio, int seqcount)
1089 hammer2_off_t size;
1090 struct m_buf *bp;
1091 int error;
1093 error = 0;
1096 * UIO read loop.
1098 * WARNING! Assumes that the kernel interlocks size changes at the
1099 * vnode level.
1101 hammer2_mtx_sh(&ip->lock);
1102 hammer2_mtx_sh(&ip->truncate_lock);
1103 size = ip->meta.size;
1104 hammer2_mtx_unlock(&ip->lock);
1106 while (uio->uio_resid > 0 && uio->uio_offset < size) {
1107 hammer2_key_t lbase;
1108 hammer2_key_t leof;
1109 int lblksize;
1110 int loff;
1111 int n;
1113 lblksize = hammer2_calc_logical(ip, uio->uio_offset,
1114 &lbase, &leof);
1115 #if 0
1116 #if 1
1117 bp = NULL;
1118 error = cluster_readx(ip->vp, leof, lbase, lblksize,
1119 B_NOTMETA | B_KVABIO,
1120 uio->uio_resid,
1121 seqcount * MAXBSIZE,
1122 &bp);
1123 #else
1124 if (uio->uio_segflg == UIO_NOCOPY) {
1125 bp = getblk(ip->vp, lbase, lblksize,
1126 GETBLK_BHEAVY | GETBLK_KVABIO, 0);
1127 if (bp->b_flags & B_CACHE) {
1128 int i;
1129 int j = 0;
1130 if (bp->b_xio.xio_npages != 16)
1131 kprintf("NPAGES BAD\n");
1132 for (i = 0; i < bp->b_xio.xio_npages; ++i) {
1133 vm_page_t m;
1134 m = bp->b_xio.xio_pages[i];
1135 if (m == NULL || m->valid == 0) {
1136 kprintf("bp %016jx %016jx pg %d inv",
1137 lbase, leof, i);
1138 if (m)
1139 kprintf("m->object %p/%p", m->object, ip->vp->v_object);
1140 kprintf("\n");
1141 j = 1;
1144 if (j)
1145 kprintf("b_flags %08x, b_error %d\n", bp->b_flags, bp->b_error);
1147 bqrelse(bp);
1149 error = bread_kvabio(ip->vp, lbase, lblksize, &bp);
1150 #endif
1151 #else
1152 bp = getblkx(ip->vp, lbase, lblksize,
1153 GETBLK_BHEAVY | GETBLK_KVABIO, 0);
1154 bp->b_cmd = BUF_CMD_READ;
1156 struct bio bio;
1157 bio.bio_buf = bp;
1158 bio.bio_offset = lbase;
1160 struct vop_strategy_args ap;
1161 ap.a_vp = ip->vp;
1162 ap.a_bio = &bio;
1164 error = hammer2_vop_strategy(&ap);
1165 assert(!error);
1166 #endif
1167 if (error) {
1168 brelse(bp);
1169 break;
1171 bkvasync(bp);
1172 loff = (int)(uio->uio_offset - lbase);
1173 n = lblksize - loff;
1174 if (n > uio->uio_resid)
1175 n = uio->uio_resid;
1176 if (n > size - uio->uio_offset)
1177 n = (int)(size - uio->uio_offset);
1178 //bp->b_flags |= B_AGE;
1179 uiomovebp(bp, bp->b_data + loff, n, uio);
1180 bqrelse(bp);
1182 hammer2_mtx_unlock(&ip->truncate_lock);
1184 return (error);
1188 * Write to the file represented by the inode via the logical buffer cache.
1189 * The inode may represent a regular file or a symlink.
1191 * The inode must not be locked.
1193 static
1195 hammer2_write_file(hammer2_inode_t *ip, struct uio *uio,
1196 int ioflag, int seqcount)
1198 hammer2_key_t old_eof;
1199 hammer2_key_t new_eof;
1200 struct m_buf *bp;
1201 int kflags;
1202 int error;
1203 int modified;
1206 * Setup if append
1208 * WARNING! Assumes that the kernel interlocks size changes at the
1209 * vnode level.
1211 hammer2_mtx_ex(&ip->lock);
1212 hammer2_mtx_sh(&ip->truncate_lock);
1213 if (ioflag & IO_APPEND)
1214 uio->uio_offset = ip->meta.size;
1215 old_eof = ip->meta.size;
1218 * Extend the file if necessary. If the write fails at some point
1219 * we will truncate it back down to cover as much as we were able
1220 * to write.
1222 * Doing this now makes it easier to calculate buffer sizes in
1223 * the loop.
1225 kflags = 0;
1226 error = 0;
1227 modified = 0;
1229 if (uio->uio_offset + uio->uio_resid > old_eof) {
1230 new_eof = uio->uio_offset + uio->uio_resid;
1231 modified = 1;
1232 hammer2_extend_file(ip, new_eof);
1233 kflags |= NOTE_EXTEND;
1234 } else {
1235 new_eof = old_eof;
1237 hammer2_mtx_unlock(&ip->lock);
1240 * UIO write loop
1242 while (uio->uio_resid > 0) {
1243 hammer2_key_t lbase;
1244 int trivial;
1245 int endofblk;
1246 int lblksize;
1247 int loff;
1248 int n;
1251 * Don't allow the buffer build to blow out the buffer
1252 * cache.
1254 if ((ioflag & IO_RECURSE) == 0)
1255 bwillwrite(HAMMER2_PBUFSIZE);
1258 * This nominally tells us how much we can cluster and
1259 * what the logical buffer size needs to be. Currently
1260 * we don't try to cluster the write and just handle one
1261 * block at a time.
1263 lblksize = hammer2_calc_logical(ip, uio->uio_offset,
1264 &lbase, NULL);
1265 loff = (int)(uio->uio_offset - lbase);
1267 KKASSERT(lblksize <= MAXBSIZE);
1270 * Calculate bytes to copy this transfer and whether the
1271 * copy completely covers the buffer or not.
1273 trivial = 0;
1274 n = lblksize - loff;
1275 if (n > uio->uio_resid) {
1276 n = uio->uio_resid;
1277 if (loff == lbase && uio->uio_offset + n == new_eof)
1278 trivial = 1;
1279 endofblk = 0;
1280 } else {
1281 if (loff == 0)
1282 trivial = 1;
1283 endofblk = 1;
1285 if (lbase >= new_eof)
1286 trivial = 1;
1287 trivial = 1; /* force trivial for makefs */
1290 * Get the buffer
1292 if (uio->uio_segflg == UIO_NOCOPY) {
1293 assert(0); /* no UIO_NOCOPY in makefs */
1295 * Issuing a write with the same data backing the
1296 * buffer. Instantiate the buffer to collect the
1297 * backing vm pages, then read-in any missing bits.
1299 * This case is used by vop_stdputpages().
1301 bp = getblkx(ip->vp, lbase, lblksize,
1302 GETBLK_BHEAVY | GETBLK_KVABIO, 0);
1304 if ((bp->b_flags & B_CACHE) == 0) {
1305 bqrelse(bp);
1306 error = bread_kvabio(ip->vp, lbase,
1307 lblksize, &bp);
1310 } else if (trivial) {
1312 * Even though we are entirely overwriting the buffer
1313 * we may still have to zero it out to avoid a
1314 * mmap/write visibility issue.
1316 bp = getblkx(ip->vp, lbase, lblksize,
1317 GETBLK_BHEAVY | GETBLK_KVABIO, 0);
1319 if ((bp->b_flags & B_CACHE) == 0)
1320 vfs_bio_clrbuf(bp);
1322 } else {
1323 assert(0); /* no partial write in makefs */
1325 * Partial overwrite, read in any missing bits then
1326 * replace the portion being written.
1328 * (The strategy code will detect zero-fill physical
1329 * blocks for this case).
1331 error = bread_kvabio(ip->vp, lbase, lblksize, &bp);
1332 if (error == 0)
1333 bheavy(bp);
1336 if (error) {
1337 brelse(bp);
1338 break;
1342 * Ok, copy the data in
1344 bkvasync(bp);
1345 error = uiomovebp(bp, bp->b_data + loff, n, uio);
1346 kflags |= NOTE_WRITE;
1347 modified = 1;
1348 if (error) {
1349 brelse(bp);
1350 break;
1354 * WARNING: Pageout daemon will issue UIO_NOCOPY writes
1355 * with IO_SYNC or IO_ASYNC set. These writes
1356 * must be handled as the pageout daemon expects.
1358 * NOTE! H2 relies on cluster_write() here because it
1359 * cannot preallocate disk blocks at the logical
1360 * level due to not knowing what the compression
1361 * size will be at this time.
1363 * We must use cluster_write() here and we depend
1364 * on the write-behind feature to flush buffers
1365 * appropriately. If we let the buffer daemons do
1366 * it the block allocations will be all over the
1367 * map.
1369 if (1) {
1370 bp->b_cmd = BUF_CMD_WRITE;
1372 struct bio bio;
1373 bio.bio_buf = bp;
1374 bio.bio_offset = lbase;
1376 struct vop_strategy_args ap;
1377 ap.a_vp = ip->vp;
1378 ap.a_bio = &bio;
1380 error = hammer2_vop_strategy(&ap);
1381 assert(!error);
1383 brelse(bp);
1384 } else if (ioflag & IO_SYNC) {
1385 assert(0);
1386 bwrite(bp);
1387 } else if ((ioflag & IO_DIRECT) && endofblk) {
1388 assert(0);
1389 bawrite(bp);
1390 } else if (ioflag & IO_ASYNC) {
1391 assert(0);
1392 bawrite(bp);
1393 } else if (0 /*ip->vp->v_mount->mnt_flag & MNT_NOCLUSTERW*/) {
1394 assert(0);
1395 bdwrite(bp);
1396 } else {
1397 assert(0);
1398 #if 0
1399 #if 1
1400 bp->b_flags |= B_CLUSTEROK;
1401 cluster_write(bp, new_eof, lblksize, seqcount);
1402 #else
1403 bp->b_flags |= B_CLUSTEROK;
1404 bdwrite(bp);
1405 #endif
1406 #endif
1411 * Cleanup. If we extended the file EOF but failed to write through
1412 * the entire write is a failure and we have to back-up.
1414 if (error && new_eof != old_eof) {
1415 hammer2_mtx_unlock(&ip->truncate_lock);
1416 hammer2_mtx_ex(&ip->lock); /* note lock order */
1417 hammer2_mtx_ex(&ip->truncate_lock); /* note lock order */
1418 hammer2_truncate_file(ip, old_eof);
1419 if (ip->flags & HAMMER2_INODE_MODIFIED)
1420 hammer2_inode_chain_sync(ip);
1421 hammer2_mtx_unlock(&ip->lock);
1422 } else if (modified) {
1423 struct m_vnode *vp = ip->vp;
1425 hammer2_mtx_ex(&ip->lock);
1426 hammer2_inode_modify(ip);
1427 if (uio->uio_segflg == UIO_NOCOPY) {
1428 assert(0); /* no UIO_NOCOPY in makefs */
1430 if (vp->v_flag & VLASTWRITETS) {
1431 ip->meta.mtime =
1432 (unsigned long)vp->v_lastwrite_ts.tv_sec *
1433 1000000 +
1434 vp->v_lastwrite_ts.tv_nsec / 1000;
1437 } else {
1438 hammer2_update_time(&ip->meta.mtime, true);
1439 vclrflags(vp, VLASTWRITETS);
1442 #if 0
1444 * REMOVED - handled by hammer2_extend_file(). Do not issue
1445 * a chain_sync() outside of a sync/fsync except for DIRECTDATA
1446 * state changes.
1448 * Under normal conditions we only issue a chain_sync if
1449 * the inode's DIRECTDATA state changed.
1451 if (ip->flags & HAMMER2_INODE_RESIZED)
1452 hammer2_inode_chain_sync(ip);
1453 #endif
1454 hammer2_mtx_unlock(&ip->lock);
1455 hammer2_knote(ip->vp, kflags);
1457 hammer2_trans_assert_strategy(ip->pmp);
1458 hammer2_mtx_unlock(&ip->truncate_lock);
1460 return error;
1464 * Truncate the size of a file. The inode must be locked.
1466 * We must unconditionally set HAMMER2_INODE_RESIZED to properly
1467 * ensure that any on-media data beyond the new file EOF has been destroyed.
1469 * WARNING: nvtruncbuf() can only be safely called without the inode lock
1470 * held due to the way our write thread works. If the truncation
1471 * occurs in the middle of a buffer, nvtruncbuf() is responsible
1472 * for dirtying that buffer and zeroing out trailing bytes.
1474 * WARNING! Assumes that the kernel interlocks size changes at the
1475 * vnode level.
1477 * WARNING! Caller assumes responsibility for removing dead blocks
1478 * if INODE_RESIZED is set.
1480 static
1481 void
1482 hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
1484 int nblksize;
1486 hammer2_mtx_unlock(&ip->lock);
1487 if (ip->vp) {
1488 nblksize = hammer2_calc_logical(ip, 0, NULL, NULL);
1489 nvtruncbuf(ip->vp, nsize,
1490 nblksize, (int)nsize & (nblksize - 1),
1493 hammer2_mtx_ex(&ip->lock);
1494 KKASSERT((ip->flags & HAMMER2_INODE_RESIZED) == 0);
1495 ip->osize = ip->meta.size;
1496 ip->meta.size = nsize;
1497 atomic_set_int(&ip->flags, HAMMER2_INODE_RESIZED);
1498 hammer2_inode_modify(ip);
1502 * Extend the size of a file. The inode must be locked.
1504 * Even though the file size is changing, we do not have to set the
1505 * INODE_RESIZED bit unless the file size crosses the EMBEDDED_BYTES
1506 * boundary. When this occurs a hammer2_inode_chain_sync() is required
1507 * to prepare the inode cluster's indirect block table, otherwise
1508 * async execution of the strategy code will implode on us.
1510 * WARNING! Assumes that the kernel interlocks size changes at the
1511 * vnode level.
1513 * WARNING! Caller assumes responsibility for transitioning out
1514 * of the inode DIRECTDATA mode if INODE_RESIZED is set.
1516 static
1517 void
1518 hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize)
1520 hammer2_key_t osize;
1521 int oblksize;
1522 int nblksize;
1523 int error;
1525 KKASSERT((ip->flags & HAMMER2_INODE_RESIZED) == 0);
1526 hammer2_inode_modify(ip);
1527 osize = ip->meta.size;
1528 ip->osize = osize;
1529 ip->meta.size = nsize;
1532 * We must issue a chain_sync() when the DIRECTDATA state changes
1533 * to prevent confusion between the flush code and the in-memory
1534 * state. This is not perfect because we are doing it outside of
1535 * a sync/fsync operation, so it might not be fully synchronized
1536 * with the meta-data topology flush.
1538 * We must retain and re-dirty the buffer cache buffer containing
1539 * the direct data so it can be written to a real block. It should
1540 * not be possible for a bread error to occur since the original data
1541 * is extracted from the inode structure directly.
1543 if (osize <= HAMMER2_EMBEDDED_BYTES && nsize > HAMMER2_EMBEDDED_BYTES) {
1544 if (osize) {
1545 assert(0); /* no such transition in makefs */
1546 struct m_buf *bp;
1548 oblksize = hammer2_calc_logical(ip, 0, NULL, NULL);
1549 error = bread_kvabio(ip->vp, 0, oblksize, &bp);
1550 atomic_set_int(&ip->flags, HAMMER2_INODE_RESIZED);
1551 hammer2_inode_chain_sync(ip);
1552 if (error == 0) {
1553 bheavy(bp);
1554 bdwrite(bp);
1555 } else {
1556 brelse(bp);
1558 } else {
1559 atomic_set_int(&ip->flags, HAMMER2_INODE_RESIZED);
1560 hammer2_inode_chain_sync(ip);
1563 hammer2_mtx_unlock(&ip->lock);
1564 if (ip->vp) {
1565 oblksize = hammer2_calc_logical(ip, 0, NULL, NULL);
1566 nblksize = hammer2_calc_logical(ip, 0, NULL, NULL);
1567 nvextendbuf(ip->vp,
1568 osize, nsize,
1569 oblksize, nblksize,
1570 -1, -1, 0);
1572 hammer2_mtx_ex(&ip->lock);
1575 static
1577 hammer2_vop_nresolve(struct vop_nresolve_args *ap)
1579 hammer2_xop_nresolve_t *xop;
1580 hammer2_inode_t *ip;
1581 hammer2_inode_t *dip;
1582 struct namecache *ncp;
1583 struct m_vnode *vp;
1584 int error;
1586 dip = VTOI(ap->a_dvp);
1587 xop = hammer2_xop_alloc(dip, 0);
1589 ncp = ap->a_nch->ncp;
1590 hammer2_xop_setname(&xop->head, ncp->nc_name, ncp->nc_nlen);
1593 * Note: In DragonFly the kernel handles '.' and '..'.
1595 hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);
1596 hammer2_xop_start(&xop->head, &hammer2_nresolve_desc);
1598 error = hammer2_xop_collect(&xop->head, 0);
1599 error = hammer2_error_to_errno(error);
1600 if (error) {
1601 ip = NULL;
1602 } else {
1603 ip = hammer2_inode_get(dip->pmp, &xop->head, -1, -1);
1605 hammer2_inode_unlock(dip);
1608 * Acquire the related vnode
1610 * NOTE: For error processing, only ENOENT resolves the namecache
1611 * entry to NULL, otherwise we just return the error and
1612 * leave the namecache unresolved.
1614 * WARNING: inode structure is locked exclusively via inode_get
1615 * but chain was locked shared. inode_unlock()
1616 * will handle it properly.
1618 if (ip) {
1619 vp = hammer2_igetv(ip, &error); /* error set to UNIX error */
1620 if (error == 0) {
1621 vn_unlock(vp);
1622 cache_setvp(ap->a_nch, vp);
1623 *ap->a_vpp = vp;
1624 } else if (error == ENOENT) {
1625 cache_setvp(ap->a_nch, NULL);
1627 hammer2_inode_unlock(ip);
1630 * The vp should not be released until after we've disposed
1631 * of our locks, because it might cause vop_inactive() to
1632 * be called.
1634 if (vp)
1635 vrele(vp);
1636 } else {
1637 error = ENOENT;
1638 cache_setvp(ap->a_nch, NULL);
1640 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
1642 KASSERT(error || ap->a_nch->ncp->nc_vp != NULL,
1643 ("resolve error %d/%p ap %p\n",
1644 error, ap->a_nch->ncp->nc_vp, ap));
1647 return error;
1651 hammer2_nresolve(struct m_vnode *dvp, struct m_vnode **vpp, char *name, int nlen)
1653 *vpp = NULL;
1654 struct namecache nc = {
1655 .nc_name = name,
1656 .nc_nlen = nlen,
1658 struct nchandle nch = {
1659 .ncp = &nc,
1661 struct vop_nresolve_args ap = {
1662 .a_nch = &nch,
1663 .a_dvp = dvp,
1664 .a_vpp = vpp,
1667 return hammer2_vop_nresolve(&ap);
1670 static
1672 hammer2_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
1674 #if 0
1675 hammer2_inode_t *dip;
1676 hammer2_tid_t inum;
1677 int error;
1679 dip = VTOI(ap->a_dvp);
1680 inum = dip->meta.iparent;
1681 *ap->a_vpp = NULL;
1683 if (inum) {
1684 error = hammer2_vfs_vget(ap->a_dvp->v_mount, NULL,
1685 inum, ap->a_vpp);
1686 } else {
1687 error = ENOENT;
1689 return error;
1690 #endif
1691 return (EOPNOTSUPP);
1694 static
1696 hammer2_vop_nmkdir(struct vop_nmkdir_args *ap)
1698 hammer2_inode_t *dip;
1699 hammer2_inode_t *nip;
1700 struct namecache *ncp;
1701 const char *name;
1702 size_t name_len;
1703 hammer2_tid_t inum;
1704 int error;
1706 dip = VTOI(ap->a_dvp);
1707 if (dip->pmp->ronly || (dip->pmp->flags & HAMMER2_PMPF_EMERG))
1708 return (EROFS);
1709 if (hammer2_vfs_enospace(dip, 0, ap->a_cred) > 1)
1710 return (ENOSPC);
1712 ncp = ap->a_nch->ncp;
1713 name = ncp->nc_name;
1714 name_len = ncp->nc_nlen;
1716 hammer2_trans_init(dip->pmp, 0);
1718 inum = hammer2_trans_newinum(dip->pmp);
1721 * Create the directory as an inode and then create the directory
1722 * entry.
1724 * dip must be locked before nip to avoid deadlock.
1726 hammer2_inode_lock(dip, 0);
1727 nip = hammer2_inode_create_normal(dip, ap->a_vap, ap->a_cred,
1728 inum, &error);
1729 if (error) {
1730 error = hammer2_error_to_errno(error);
1731 } else {
1732 error = hammer2_dirent_create(dip, name, name_len,
1733 nip->meta.inum, nip->meta.type);
1734 /* returns UNIX error code */
1736 if (error) {
1737 if (nip) {
1738 hammer2_inode_unlink_finisher(nip, NULL);
1739 hammer2_inode_unlock(nip);
1740 nip = NULL;
1742 *ap->a_vpp = NULL;
1743 } else {
1745 * inode_depend() must occur before the igetv() because
1746 * the igetv() can temporarily release the inode lock.
1748 hammer2_inode_depend(dip, nip); /* before igetv */
1749 *ap->a_vpp = hammer2_igetv(nip, &error);
1750 hammer2_inode_unlock(nip);
1754 * Update dip's mtime
1756 * We can use a shared inode lock and allow the meta.mtime update
1757 * SMP race. hammer2_inode_modify() is MPSAFE w/a shared lock.
1759 if (error == 0) {
1760 uint64_t mtime;
1762 /*hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);*/
1763 hammer2_update_time(&mtime, true);
1764 hammer2_inode_modify(dip);
1765 dip->meta.mtime = mtime;
1766 /*hammer2_inode_unlock(dip);*/
1768 hammer2_inode_unlock(dip);
1770 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
1772 if (error == 0) {
1773 cache_setunresolved(ap->a_nch);
1774 cache_setvp(ap->a_nch, *ap->a_vpp);
1775 hammer2_knote(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
1777 return error;
1781 hammer2_nmkdir(struct m_vnode *dvp, struct m_vnode **vpp, char *name, int nlen,
1782 mode_t mode)
1784 struct namecache nc = {
1785 .nc_name = name,
1786 .nc_nlen = nlen,
1788 struct nchandle nch = {
1789 .ncp = &nc,
1791 uid_t va_uid = VNOVAL; //getuid();
1792 uid_t va_gid = VNOVAL; //getgid();
1793 struct vattr va = {
1794 .va_type = VDIR,
1795 .va_mode = mode & ~S_IFMT,
1796 .va_uid = va_uid,
1797 .va_gid = va_gid,
1799 struct vop_nmkdir_args ap = {
1800 .a_nch = &nch,
1801 .a_dvp = dvp,
1802 .a_vpp = vpp,
1803 .a_vap = &va,
1806 return hammer2_vop_nmkdir(&ap);
1809 static
1811 hammer2_vop_open(struct vop_open_args *ap)
1813 #if 0
1814 return vop_stdopen(ap);
1815 #endif
1816 return (EOPNOTSUPP);
1820 * hammer2_vop_advlock { vp, id, op, fl, flags }
1822 static
1824 hammer2_vop_advlock(struct vop_advlock_args *ap)
1826 #if 0
1827 hammer2_inode_t *ip = VTOI(ap->a_vp);
1828 hammer2_off_t size;
1830 size = ip->meta.size;
1831 return (lf_advlock(ap, &ip->advlock, size));
1832 #endif
1833 return (EOPNOTSUPP);
1836 static
1838 hammer2_vop_close(struct vop_close_args *ap)
1840 #if 0
1841 return vop_stdclose(ap);
1842 #endif
1843 return (EOPNOTSUPP);
1847 * hammer2_vop_nlink { nch, dvp, vp, cred }
1849 * Create a hardlink from (vp) to {dvp, nch}.
1851 static
1853 hammer2_vop_nlink(struct vop_nlink_args *ap)
1855 hammer2_inode_t *tdip; /* target directory to create link in */
1856 hammer2_inode_t *ip; /* inode we are hardlinking to */
1857 struct namecache *ncp;
1858 const char *name;
1859 size_t name_len;
1860 int error;
1861 uint64_t cmtime;
1863 /* We know it's the same in makefs */
1865 if (ap->a_dvp->v_mount != ap->a_vp->v_mount)
1866 return(EXDEV);
1869 tdip = VTOI(ap->a_dvp);
1870 if (tdip->pmp->ronly || (tdip->pmp->flags & HAMMER2_PMPF_EMERG))
1871 return (EROFS);
1872 if (hammer2_vfs_enospace(tdip, 0, ap->a_cred) > 1)
1873 return (ENOSPC);
1875 ncp = ap->a_nch->ncp;
1876 name = ncp->nc_name;
1877 name_len = ncp->nc_nlen;
1880 * ip represents the file being hardlinked. The file could be a
1881 * normal file or a hardlink target if it has already been hardlinked.
1882 * (with the new semantics, it will almost always be a hardlink
1883 * target).
1885 * Bump nlinks and potentially also create or move the hardlink
1886 * target in the parent directory common to (ip) and (tdip). The
1887 * consolidation code can modify ip->cluster. The returned cluster
1888 * is locked.
1890 ip = VTOI(ap->a_vp);
1891 KASSERT(ip->pmp, ("ip->pmp is NULL %p %p", ip, ip->pmp));
1892 hammer2_trans_init(ip->pmp, 0);
1895 * Target should be an indexed inode or there's no way we will ever
1896 * be able to find it!
1898 KKASSERT((ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) == 0);
1900 hammer2_inode_lock4(tdip, ip, NULL, NULL);
1902 hammer2_update_time(&cmtime, true);
1905 * Create the directory entry and bump nlinks.
1906 * Also update ip's ctime.
1908 error = hammer2_dirent_create(tdip, name, name_len,
1909 ip->meta.inum, ip->meta.type);
1910 hammer2_inode_modify(ip);
1911 ++ip->meta.nlinks;
1912 ip->meta.ctime = cmtime;
1913 if (error == 0) {
1915 * Update dip's [cm]time
1917 hammer2_inode_modify(tdip);
1918 tdip->meta.mtime = cmtime;
1919 tdip->meta.ctime = cmtime;
1921 cache_setunresolved(ap->a_nch);
1922 cache_setvp(ap->a_nch, ap->a_vp);
1924 hammer2_inode_unlock(ip);
1925 hammer2_inode_unlock(tdip);
1927 hammer2_trans_done(ip->pmp, HAMMER2_TRANS_SIDEQ);
1928 hammer2_knote(ap->a_vp, NOTE_LINK);
1929 hammer2_knote(ap->a_dvp, NOTE_WRITE);
1931 return error;
1935 hammer2_nlink(struct m_vnode *dvp, struct m_vnode *vp, char *name, int nlen)
1937 struct namecache nc = {
1938 .nc_name = name,
1939 .nc_nlen = nlen,
1941 struct nchandle nch = {
1942 .ncp = &nc,
1944 struct vop_nlink_args ap = {
1945 .a_nch = &nch,
1946 .a_dvp = dvp,
1947 .a_vp = vp,
1950 return hammer2_vop_nlink(&ap);
1954 * hammer2_vop_ncreate { nch, dvp, vpp, cred, vap }
1956 * The operating system has already ensured that the directory entry
1957 * does not exist and done all appropriate namespace locking.
1959 static
1961 hammer2_vop_ncreate(struct vop_ncreate_args *ap)
1963 hammer2_inode_t *dip;
1964 hammer2_inode_t *nip;
1965 struct namecache *ncp;
1966 const char *name;
1967 size_t name_len;
1968 hammer2_tid_t inum;
1969 int error;
1971 dip = VTOI(ap->a_dvp);
1972 if (dip->pmp->ronly || (dip->pmp->flags & HAMMER2_PMPF_EMERG))
1973 return (EROFS);
1974 if (hammer2_vfs_enospace(dip, 0, ap->a_cred) > 1)
1975 return (ENOSPC);
1977 ncp = ap->a_nch->ncp;
1978 name = ncp->nc_name;
1979 name_len = ncp->nc_nlen;
1980 hammer2_trans_init(dip->pmp, 0);
1982 inum = hammer2_trans_newinum(dip->pmp);
1985 * Create the regular file as an inode and then create the directory
1986 * entry.
1988 * dip must be locked before nip to avoid deadlock.
1990 hammer2_inode_lock(dip, 0);
1991 nip = hammer2_inode_create_normal(dip, ap->a_vap, ap->a_cred,
1992 inum, &error);
1993 if (error) {
1994 error = hammer2_error_to_errno(error);
1995 } else {
1996 error = hammer2_dirent_create(dip, name, name_len,
1997 nip->meta.inum, nip->meta.type);
1999 if (error) {
2000 if (nip) {
2001 hammer2_inode_unlink_finisher(nip, NULL);
2002 hammer2_inode_unlock(nip);
2003 nip = NULL;
2005 *ap->a_vpp = NULL;
2006 } else {
2007 hammer2_inode_depend(dip, nip); /* before igetv */
2008 *ap->a_vpp = hammer2_igetv(nip, &error);
2009 hammer2_inode_unlock(nip);
2013 * Update dip's mtime
2015 if (error == 0) {
2016 uint64_t mtime;
2018 /*hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);*/
2019 hammer2_update_time(&mtime, true);
2020 hammer2_inode_modify(dip);
2021 dip->meta.mtime = mtime;
2022 /*hammer2_inode_unlock(dip);*/
2024 hammer2_inode_unlock(dip);
2026 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
2028 if (error == 0) {
2029 cache_setunresolved(ap->a_nch);
2030 cache_setvp(ap->a_nch, *ap->a_vpp);
2031 hammer2_knote(ap->a_dvp, NOTE_WRITE);
2033 return error;
2037 hammer2_ncreate(struct m_vnode *dvp, struct m_vnode **vpp, char *name, int nlen,
2038 mode_t mode)
2040 struct namecache nc = {
2041 .nc_name = name,
2042 .nc_nlen = nlen,
2044 struct nchandle nch = {
2045 .ncp = &nc,
2047 uid_t va_uid = VNOVAL; //getuid();
2048 uid_t va_gid = VNOVAL; //getgid();
2049 struct vattr va = {
2050 .va_type = VREG,
2051 .va_mode = mode & ~S_IFMT,
2052 .va_uid = va_uid,
2053 .va_gid = va_gid,
2055 struct vop_ncreate_args ap = {
2056 .a_nch = &nch,
2057 .a_dvp = dvp,
2058 .a_vpp = vpp,
2059 .a_vap = &va,
2062 return hammer2_vop_ncreate(&ap);
2066 * Make a device node (typically a fifo)
2068 static
2070 hammer2_vop_nmknod(struct vop_nmknod_args *ap)
2072 hammer2_inode_t *dip;
2073 hammer2_inode_t *nip;
2074 struct namecache *ncp;
2075 const char *name;
2076 size_t name_len;
2077 hammer2_tid_t inum;
2078 int error;
2080 dip = VTOI(ap->a_dvp);
2081 if (dip->pmp->ronly || (dip->pmp->flags & HAMMER2_PMPF_EMERG))
2082 return (EROFS);
2083 if (hammer2_vfs_enospace(dip, 0, ap->a_cred) > 1)
2084 return (ENOSPC);
2086 ncp = ap->a_nch->ncp;
2087 name = ncp->nc_name;
2088 name_len = ncp->nc_nlen;
2089 hammer2_trans_init(dip->pmp, 0);
2092 * Create the device inode and then create the directory entry.
2094 * dip must be locked before nip to avoid deadlock.
2096 inum = hammer2_trans_newinum(dip->pmp);
2098 hammer2_inode_lock(dip, 0);
2099 nip = hammer2_inode_create_normal(dip, ap->a_vap, ap->a_cred,
2100 inum, &error);
2101 if (error) {
2102 error = hammer2_error_to_errno(error);
2103 } else {
2104 error = hammer2_dirent_create(dip, name, name_len,
2105 nip->meta.inum, nip->meta.type);
2107 if (error) {
2108 if (nip) {
2109 hammer2_inode_unlink_finisher(nip, NULL);
2110 hammer2_inode_unlock(nip);
2111 nip = NULL;
2113 *ap->a_vpp = NULL;
2114 } else {
2115 hammer2_inode_depend(dip, nip); /* before igetv */
2116 *ap->a_vpp = hammer2_igetv(nip, &error);
2117 hammer2_inode_unlock(nip);
2121 * Update dip's mtime
2123 if (error == 0) {
2124 uint64_t mtime;
2126 /*hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);*/
2127 hammer2_update_time(&mtime, true);
2128 hammer2_inode_modify(dip);
2129 dip->meta.mtime = mtime;
2130 /*hammer2_inode_unlock(dip);*/
2132 hammer2_inode_unlock(dip);
2134 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
2136 if (error == 0) {
2137 cache_setunresolved(ap->a_nch);
2138 cache_setvp(ap->a_nch, *ap->a_vpp);
2139 hammer2_knote(ap->a_dvp, NOTE_WRITE);
2141 return error;
2145 hammer2_nmknod(struct m_vnode *dvp, struct m_vnode **vpp, char *name, int nlen,
2146 int type, mode_t mode)
2148 struct namecache nc = {
2149 .nc_name = name,
2150 .nc_nlen = nlen,
2152 struct nchandle nch = {
2153 .ncp = &nc,
2155 uid_t va_uid = VNOVAL; //getuid();
2156 uid_t va_gid = VNOVAL; //getgid();
2157 struct vattr va = {
2158 .va_type = type,
2159 .va_mode = mode & ~S_IFMT,
2160 .va_uid = va_uid,
2161 .va_gid = va_gid,
2163 struct vop_nmknod_args ap = {
2164 .a_nch = &nch,
2165 .a_dvp = dvp,
2166 .a_vpp = vpp,
2167 .a_vap = &va,
2170 return hammer2_vop_nmknod(&ap);
2174 * hammer2_vop_nsymlink { nch, dvp, vpp, cred, vap, target }
2176 static
2178 hammer2_vop_nsymlink(struct vop_nsymlink_args *ap)
2180 hammer2_inode_t *dip;
2181 hammer2_inode_t *nip;
2182 struct namecache *ncp;
2183 const char *name;
2184 size_t name_len;
2185 hammer2_tid_t inum;
2186 int error;
2188 dip = VTOI(ap->a_dvp);
2189 if (dip->pmp->ronly || (dip->pmp->flags & HAMMER2_PMPF_EMERG))
2190 return (EROFS);
2191 if (hammer2_vfs_enospace(dip, 0, ap->a_cred) > 1)
2192 return (ENOSPC);
2194 ncp = ap->a_nch->ncp;
2195 name = ncp->nc_name;
2196 name_len = ncp->nc_nlen;
2197 hammer2_trans_init(dip->pmp, 0);
2199 ap->a_vap->va_type = VLNK; /* enforce type */
2202 * Create the softlink as an inode and then create the directory
2203 * entry.
2205 * dip must be locked before nip to avoid deadlock.
2207 inum = hammer2_trans_newinum(dip->pmp);
2209 hammer2_inode_lock(dip, 0);
2210 nip = hammer2_inode_create_normal(dip, ap->a_vap, ap->a_cred,
2211 inum, &error);
2212 if (error) {
2213 error = hammer2_error_to_errno(error);
2214 } else {
2215 error = hammer2_dirent_create(dip, name, name_len,
2216 nip->meta.inum, nip->meta.type);
2218 if (error) {
2219 if (nip) {
2220 hammer2_inode_unlink_finisher(nip, NULL);
2221 hammer2_inode_unlock(nip);
2222 nip = NULL;
2224 *ap->a_vpp = NULL;
2225 hammer2_inode_unlock(dip);
2226 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
2227 return error;
2229 hammer2_inode_depend(dip, nip); /* before igetv */
2230 *ap->a_vpp = hammer2_igetv(nip, &error);
2233 * Build the softlink (~like file data) and finalize the namecache.
2235 if (error == 0) {
2236 size_t bytes;
2237 struct uio auio;
2238 struct iovec aiov;
2240 bytes = strlen(ap->a_target);
2242 hammer2_inode_unlock(nip);
2243 bzero(&auio, sizeof(auio));
2244 bzero(&aiov, sizeof(aiov));
2245 auio.uio_iov = &aiov;
2246 auio.uio_segflg = UIO_SYSSPACE;
2247 auio.uio_rw = UIO_WRITE;
2248 auio.uio_resid = bytes;
2249 auio.uio_iovcnt = 1;
2250 auio.uio_td = curthread;
2251 aiov.iov_base = ap->a_target;
2252 aiov.iov_len = bytes;
2253 error = hammer2_write_file(nip, &auio, IO_APPEND, 0);
2254 /* XXX handle error */
2255 error = 0;
2256 } else {
2257 hammer2_inode_unlock(nip);
2261 * Update dip's mtime
2263 if (error == 0) {
2264 uint64_t mtime;
2266 /*hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);*/
2267 hammer2_update_time(&mtime, true);
2268 hammer2_inode_modify(dip);
2269 dip->meta.mtime = mtime;
2270 /*hammer2_inode_unlock(dip);*/
2272 hammer2_inode_unlock(dip);
2274 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
2277 * Finalize namecache
2279 if (error == 0) {
2280 cache_setunresolved(ap->a_nch);
2281 cache_setvp(ap->a_nch, *ap->a_vpp);
2282 hammer2_knote(ap->a_dvp, NOTE_WRITE);
2284 return error;
2288 hammer2_nsymlink(struct m_vnode *dvp, struct m_vnode **vpp, char *name, int nlen,
2289 char *target, mode_t mode)
2291 struct namecache nc = {
2292 .nc_name = name,
2293 .nc_nlen = nlen,
2295 struct nchandle nch = {
2296 .ncp = &nc,
2298 uid_t va_uid = VNOVAL; //getuid();
2299 uid_t va_gid = VNOVAL; //getgid();
2300 struct vattr va = {
2301 .va_type = VDIR,
2302 .va_mode = mode & ~S_IFMT,
2303 .va_uid = va_uid,
2304 .va_gid = va_gid,
2306 struct vop_nsymlink_args ap = {
2307 .a_nch = &nch,
2308 .a_dvp = dvp,
2309 .a_vpp = vpp,
2310 .a_vap = &va,
2311 .a_target = target,
2314 return hammer2_vop_nsymlink(&ap);
2318 * hammer2_vop_nremove { nch, dvp, cred }
2320 static
2322 hammer2_vop_nremove(struct vop_nremove_args *ap)
2324 #if 0
2325 hammer2_xop_unlink_t *xop;
2326 hammer2_inode_t *dip;
2327 hammer2_inode_t *ip;
2328 struct m_vnode *vprecycle;
2329 struct namecache *ncp;
2330 int error;
2332 dip = VTOI(ap->a_dvp);
2333 if (dip->pmp->ronly)
2334 return (EROFS);
2335 #if 0
2336 /* allow removals, except user to also bulkfree */
2337 if (hammer2_vfs_enospace(dip, 0, ap->a_cred) > 1)
2338 return (ENOSPC);
2339 #endif
2341 ncp = ap->a_nch->ncp;
2343 if (hammer2_debug_inode && dip->meta.inum == hammer2_debug_inode) {
2344 kprintf("hammer2: attempt to delete inside debug inode: %s\n",
2345 ncp->nc_name);
2346 while (hammer2_debug_inode &&
2347 dip->meta.inum == hammer2_debug_inode) {
2348 tsleep(&hammer2_debug_inode, 0, "h2debug", hz*5);
2352 hammer2_trans_init(dip->pmp, 0);
2353 hammer2_inode_lock(dip, 0);
2356 * The unlink XOP unlinks the path from the directory and
2357 * locates and returns the cluster associated with the real inode.
2358 * We have to handle nlinks here on the frontend.
2360 xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
2361 hammer2_xop_setname(&xop->head, ncp->nc_name, ncp->nc_nlen);
2363 xop->isdir = 0;
2364 xop->dopermanent = 0;
2365 hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
2368 * Collect the real inode and adjust nlinks, destroy the real
2369 * inode if nlinks transitions to 0 and it was the real inode
2370 * (else it has already been removed).
2372 error = hammer2_xop_collect(&xop->head, 0);
2373 error = hammer2_error_to_errno(error);
2374 vprecycle = NULL;
2376 if (error == 0) {
2377 ip = hammer2_inode_get(dip->pmp, &xop->head, -1, -1);
2378 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
2379 if (ip) {
2380 if (hammer2_debug_inode &&
2381 ip->meta.inum == hammer2_debug_inode) {
2382 kprintf("hammer2: attempt to delete debug "
2383 "inode!\n");
2384 while (hammer2_debug_inode &&
2385 ip->meta.inum == hammer2_debug_inode) {
2386 tsleep(&hammer2_debug_inode, 0,
2387 "h2debug", hz*5);
2390 hammer2_inode_unlink_finisher(ip, &vprecycle);
2391 hammer2_inode_depend(dip, ip); /* after modified */
2392 hammer2_inode_unlock(ip);
2394 } else {
2395 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
2399 * Update dip's mtime
2401 if (error == 0) {
2402 uint64_t mtime;
2404 /*hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);*/
2405 hammer2_update_time(&mtime);
2406 hammer2_inode_modify(dip);
2407 dip->meta.mtime = mtime;
2408 /*hammer2_inode_unlock(dip);*/
2410 hammer2_inode_unlock(dip);
2412 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
2413 if (error == 0) {
2414 cache_unlink(ap->a_nch);
2415 hammer2_knote(ap->a_dvp, NOTE_WRITE);
2417 if (vprecycle)
2418 hammer2_inode_vprecycle(vprecycle);
2420 return (error);
2421 #endif
2422 return (EOPNOTSUPP);
2426 * hammer2_vop_nrmdir { nch, dvp, cred }
2428 static
2430 hammer2_vop_nrmdir(struct vop_nrmdir_args *ap)
2432 #if 0
2433 hammer2_xop_unlink_t *xop;
2434 hammer2_inode_t *dip;
2435 hammer2_inode_t *ip;
2436 struct namecache *ncp;
2437 struct m_vnode *vprecycle;
2438 int error;
2440 dip = VTOI(ap->a_dvp);
2441 if (dip->pmp->ronly)
2442 return (EROFS);
2443 #if 0
2444 /* allow removals, except user to also bulkfree */
2445 if (hammer2_vfs_enospace(dip, 0, ap->a_cred) > 1)
2446 return (ENOSPC);
2447 #endif
2449 hammer2_trans_init(dip->pmp, 0);
2450 hammer2_inode_lock(dip, 0);
2452 xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
2454 ncp = ap->a_nch->ncp;
2455 hammer2_xop_setname(&xop->head, ncp->nc_name, ncp->nc_nlen);
2456 xop->isdir = 1;
2457 xop->dopermanent = 0;
2458 hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
2461 * Collect the real inode and adjust nlinks, destroy the real
2462 * inode if nlinks transitions to 0 and it was the real inode
2463 * (else it has already been removed).
2465 error = hammer2_xop_collect(&xop->head, 0);
2466 error = hammer2_error_to_errno(error);
2467 vprecycle = NULL;
2469 if (error == 0) {
2470 ip = hammer2_inode_get(dip->pmp, &xop->head, -1, -1);
2471 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
2472 if (ip) {
2473 hammer2_inode_unlink_finisher(ip, &vprecycle);
2474 hammer2_inode_depend(dip, ip); /* after modified */
2475 hammer2_inode_unlock(ip);
2477 } else {
2478 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
2482 * Update dip's mtime
2484 if (error == 0) {
2485 uint64_t mtime;
2487 /*hammer2_inode_lock(dip, HAMMER2_RESOLVE_SHARED);*/
2488 hammer2_update_time(&mtime);
2489 hammer2_inode_modify(dip);
2490 dip->meta.mtime = mtime;
2491 /*hammer2_inode_unlock(dip);*/
2493 hammer2_inode_unlock(dip);
2495 hammer2_trans_done(dip->pmp, HAMMER2_TRANS_SIDEQ);
2496 if (error == 0) {
2497 cache_unlink(ap->a_nch);
2498 hammer2_knote(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
2500 if (vprecycle)
2501 hammer2_inode_vprecycle(vprecycle);
2502 return (error);
2503 #endif
2504 return (EOPNOTSUPP);
2508 * hammer2_vop_nrename { fnch, tnch, fdvp, tdvp, cred }
2510 static
2512 hammer2_vop_nrename(struct vop_nrename_args *ap)
2514 #if 0
2515 struct namecache *fncp;
2516 struct namecache *tncp;
2517 hammer2_inode_t *fdip; /* source directory */
2518 hammer2_inode_t *tdip; /* target directory */
2519 hammer2_inode_t *ip; /* file being renamed */
2520 hammer2_inode_t *tip; /* replaced target during rename or NULL */
2521 struct m_vnode *vprecycle;
2522 const char *fname;
2523 size_t fname_len;
2524 const char *tname;
2525 size_t tname_len;
2526 int error;
2527 int update_tdip;
2528 int update_fdip;
2529 hammer2_key_t tlhc;
2531 if (ap->a_fdvp->v_mount != ap->a_tdvp->v_mount)
2532 return(EXDEV);
2533 if (ap->a_fdvp->v_mount != ap->a_fnch->ncp->nc_vp->v_mount)
2534 return(EXDEV);
2536 fdip = VTOI(ap->a_fdvp); /* source directory */
2537 tdip = VTOI(ap->a_tdvp); /* target directory */
2539 if (fdip->pmp->ronly || (fdip->pmp->flags & HAMMER2_PMPF_EMERG))
2540 return (EROFS);
2541 if (hammer2_vfs_enospace(fdip, 0, ap->a_cred) > 1)
2542 return (ENOSPC);
2544 fncp = ap->a_fnch->ncp; /* entry name in source */
2545 fname = fncp->nc_name;
2546 fname_len = fncp->nc_nlen;
2548 tncp = ap->a_tnch->ncp; /* entry name in target */
2549 tname = tncp->nc_name;
2550 tname_len = tncp->nc_nlen;
2552 hammer2_trans_init(tdip->pmp, 0);
2554 update_tdip = 0;
2555 update_fdip = 0;
2557 ip = VTOI(fncp->nc_vp);
2558 hammer2_inode_ref(ip); /* extra ref */
2561 * Lookup the target name to determine if a directory entry
2562 * is being overwritten. We only hold related inode locks
2563 * temporarily, the operating system is expected to protect
2564 * against rename races.
2566 tip = tncp->nc_vp ? VTOI(tncp->nc_vp) : NULL;
2567 if (tip)
2568 hammer2_inode_ref(tip); /* extra ref */
2571 * For now try to avoid deadlocks with a simple pointer address
2572 * test. (tip) can be NULL.
2574 error = 0;
2576 hammer2_inode_t *ip1 = fdip;
2577 hammer2_inode_t *ip2 = tdip;
2578 hammer2_inode_t *ip3 = ip;
2579 hammer2_inode_t *ip4 = tip; /* may be NULL */
2581 if (fdip > tdip) {
2582 ip1 = tdip;
2583 ip2 = fdip;
2585 if (tip && ip > tip) {
2586 ip3 = tip;
2587 ip4 = ip;
2589 hammer2_inode_lock4(ip1, ip2, ip3, ip4);
2593 * Resolve the collision space for (tdip, tname, tname_len)
2595 * tdip must be held exclusively locked to prevent races since
2596 * multiple filenames can end up in the same collision space.
2599 hammer2_xop_scanlhc_t *sxop;
2600 hammer2_key_t lhcbase;
2602 tlhc = hammer2_dirhash(tname, tname_len);
2603 lhcbase = tlhc;
2604 sxop = hammer2_xop_alloc(tdip, HAMMER2_XOP_MODIFYING);
2605 sxop->lhc = tlhc;
2606 hammer2_xop_start(&sxop->head, &hammer2_scanlhc_desc);
2607 while ((error = hammer2_xop_collect(&sxop->head, 0)) == 0) {
2608 if (tlhc != sxop->head.cluster.focus->bref.key)
2609 break;
2610 ++tlhc;
2612 error = hammer2_error_to_errno(error);
2613 hammer2_xop_retire(&sxop->head, HAMMER2_XOPMASK_VOP);
2615 if (error) {
2616 if (error != ENOENT)
2617 goto done2;
2618 ++tlhc;
2619 error = 0;
2621 if ((lhcbase ^ tlhc) & ~HAMMER2_DIRHASH_LOMASK) {
2622 error = ENOSPC;
2623 goto done2;
2628 * Ready to go, issue the rename to the backend. Note that meta-data
2629 * updates to the related inodes occur separately from the rename
2630 * operation.
2632 * NOTE: While it is not necessary to update ip->meta.name*, doing
2633 * so aids catastrophic recovery and debugging.
2635 if (error == 0) {
2636 hammer2_xop_nrename_t *xop4;
2638 xop4 = hammer2_xop_alloc(fdip, HAMMER2_XOP_MODIFYING);
2639 xop4->lhc = tlhc;
2640 xop4->ip_key = ip->meta.name_key;
2641 hammer2_xop_setip2(&xop4->head, ip);
2642 hammer2_xop_setip3(&xop4->head, tdip);
2643 if (tip && tip->meta.type == HAMMER2_OBJTYPE_DIRECTORY)
2644 hammer2_xop_setip4(&xop4->head, tip);
2645 hammer2_xop_setname(&xop4->head, fname, fname_len);
2646 hammer2_xop_setname2(&xop4->head, tname, tname_len);
2647 hammer2_xop_start(&xop4->head, &hammer2_nrename_desc);
2649 error = hammer2_xop_collect(&xop4->head, 0);
2650 error = hammer2_error_to_errno(error);
2651 hammer2_xop_retire(&xop4->head, HAMMER2_XOPMASK_VOP);
2653 if (error == ENOENT)
2654 error = 0;
2657 * Update inode meta-data.
2659 * WARNING! The in-memory inode (ip) structure does not
2660 * maintain a copy of the inode's filename buffer.
2662 if (error == 0 &&
2663 (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) {
2664 hammer2_inode_modify(ip);
2665 ip->meta.name_len = tname_len;
2666 ip->meta.name_key = tlhc;
2668 if (error == 0) {
2669 hammer2_inode_modify(ip);
2670 ip->meta.iparent = tdip->meta.inum;
2672 update_fdip = 1;
2673 update_tdip = 1;
2676 done2:
2678 * If no error, the backend has replaced the target directory entry.
2679 * We must adjust nlinks on the original replace target if it exists.
2681 vprecycle = NULL;
2682 if (error == 0 && tip) {
2683 hammer2_inode_unlink_finisher(tip, &vprecycle);
2687 * Update directory mtimes to represent the something changed.
2689 if (update_fdip || update_tdip) {
2690 uint64_t mtime;
2692 hammer2_update_time(&mtime);
2693 if (update_fdip) {
2694 hammer2_inode_modify(fdip);
2695 fdip->meta.mtime = mtime;
2697 if (update_tdip) {
2698 hammer2_inode_modify(tdip);
2699 tdip->meta.mtime = mtime;
2702 if (tip) {
2703 hammer2_inode_unlock(tip);
2704 hammer2_inode_drop(tip);
2706 hammer2_inode_unlock(ip);
2707 hammer2_inode_unlock(tdip);
2708 hammer2_inode_unlock(fdip);
2709 hammer2_inode_drop(ip);
2710 hammer2_trans_done(tdip->pmp, HAMMER2_TRANS_SIDEQ);
2713 * Issue the namecache update after unlocking all the internal
2714 * hammer2 structures, otherwise we might deadlock.
2716 * WARNING! The target namespace must be updated atomically,
2717 * and we depend on cache_rename() to handle that for
2718 * us. Do not do a separate cache_unlink() because
2719 * that leaves a small window of opportunity for other
2720 * threads to allocate the target namespace before we
2721 * manage to complete our rename.
2723 * WARNING! cache_rename() (and cache_unlink()) will properly
2724 * set VREF_FINALIZE on any attached vnode. Do not
2725 * call cache_setunresolved() manually before-hand as
2726 * this will prevent the flag from being set later via
2727 * cache_rename(). If VREF_FINALIZE is not properly set
2728 * and the inode is no longer in the topology, related
2729 * chains can remain dirty indefinitely.
2731 if (error == 0 && tip) {
2732 /*cache_unlink(ap->a_tnch); see above */
2733 /*cache_setunresolved(ap->a_tnch); see above */
2735 if (error == 0) {
2736 cache_rename(ap->a_fnch, ap->a_tnch);
2737 hammer2_knote(ap->a_fdvp, NOTE_WRITE);
2738 hammer2_knote(ap->a_tdvp, NOTE_WRITE);
2739 hammer2_knote(fncp->nc_vp, NOTE_RENAME);
2741 if (vprecycle)
2742 hammer2_inode_vprecycle(vprecycle);
2744 return (error);
2745 #endif
2746 return (EOPNOTSUPP);
2750 * hammer2_vop_ioctl { vp, command, data, fflag, cred }
2752 static
2754 hammer2_vop_ioctl(struct vop_ioctl_args *ap)
2756 #if 0
2757 hammer2_inode_t *ip;
2758 int error;
2760 ip = VTOI(ap->a_vp);
2762 error = hammer2_ioctl(ip, ap->a_command, (void *)ap->a_data,
2763 ap->a_fflag, ap->a_cred);
2764 return (error);
2765 #endif
2766 return (EOPNOTSUPP);
2769 static
2771 hammer2_vop_mountctl(struct vop_mountctl_args *ap)
2773 #if 0
2774 struct mount *mp;
2775 hammer2_pfs_t *pmp;
2776 int rc;
2778 switch (ap->a_op) {
2779 case (MOUNTCTL_SET_EXPORT):
2780 mp = ap->a_head.a_ops->head.vv_mount;
2781 pmp = MPTOPMP(mp);
2783 if (ap->a_ctllen != sizeof(struct export_args))
2784 rc = (EINVAL);
2785 else
2786 rc = vfs_export(mp, &pmp->export,
2787 (const struct export_args *)ap->a_ctl);
2788 break;
2789 default:
2790 rc = vop_stdmountctl(ap);
2791 break;
2793 return (rc);
2794 #endif
2795 return (EOPNOTSUPP);
2799 * KQFILTER
2802 static void filt_hammer2detach(struct knote *kn);
2803 static int filt_hammer2read(struct knote *kn, long hint);
2804 static int filt_hammer2write(struct knote *kn, long hint);
2805 static int filt_hammer2vnode(struct knote *kn, long hint);
2807 static struct filterops hammer2read_filtops =
2808 { FILTEROP_ISFD | FILTEROP_MPSAFE,
2809 NULL, filt_hammer2detach, filt_hammer2read };
2810 static struct filterops hammer2write_filtops =
2811 { FILTEROP_ISFD | FILTEROP_MPSAFE,
2812 NULL, filt_hammer2detach, filt_hammer2write };
2813 static struct filterops hammer2vnode_filtops =
2814 { FILTEROP_ISFD | FILTEROP_MPSAFE,
2815 NULL, filt_hammer2detach, filt_hammer2vnode };
2818 static
2820 hammer2_vop_kqfilter(struct vop_kqfilter_args *ap)
2822 #if 0
2823 struct m_vnode *vp = ap->a_vp;
2824 struct knote *kn = ap->a_kn;
2826 switch (kn->kn_filter) {
2827 case EVFILT_READ:
2828 kn->kn_fop = &hammer2read_filtops;
2829 break;
2830 case EVFILT_WRITE:
2831 kn->kn_fop = &hammer2write_filtops;
2832 break;
2833 case EVFILT_VNODE:
2834 kn->kn_fop = &hammer2vnode_filtops;
2835 break;
2836 default:
2837 return (EOPNOTSUPP);
2840 kn->kn_hook = (caddr_t)vp;
2842 knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
2844 return(0);
2845 #endif
2846 return (EOPNOTSUPP);
2849 #if 0
2850 static void
2851 filt_hammer2detach(struct knote *kn)
2853 struct m_vnode *vp = (void *)kn->kn_hook;
2855 knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
2858 static int
2859 filt_hammer2read(struct knote *kn, long hint)
2861 struct m_vnode *vp = (void *)kn->kn_hook;
2862 hammer2_inode_t *ip = VTOI(vp);
2863 off_t off;
2865 if (hint == NOTE_REVOKE) {
2866 kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
2867 return(1);
2869 off = ip->meta.size - kn->kn_fp->f_offset;
2870 kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
2871 if (kn->kn_sfflags & NOTE_OLDAPI)
2872 return(1);
2873 return (kn->kn_data != 0);
2877 static int
2878 filt_hammer2write(struct knote *kn, long hint)
2880 if (hint == NOTE_REVOKE)
2881 kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
2882 kn->kn_data = 0;
2883 return (1);
2886 static int
2887 filt_hammer2vnode(struct knote *kn, long hint)
2889 if (kn->kn_sfflags & hint)
2890 kn->kn_fflags |= hint;
2891 if (hint == NOTE_REVOKE) {
2892 kn->kn_flags |= (EV_EOF | EV_NODATA);
2893 return (1);
2895 return (kn->kn_fflags != 0);
2897 #endif
2900 * FIFO VOPS
2902 static
2904 hammer2_vop_markatime(struct vop_markatime_args *ap)
2906 #if 0
2907 hammer2_inode_t *ip;
2908 struct m_vnode *vp;
2910 vp = ap->a_vp;
2911 ip = VTOI(vp);
2913 if (ip->pmp->ronly || (ip->pmp->flags & HAMMER2_PMPF_EMERG))
2914 return (EROFS);
2915 return(0);
2916 #endif
2917 return (EOPNOTSUPP);
2920 static
2922 hammer2_vop_fifokqfilter(struct vop_kqfilter_args *ap)
2924 #if 0
2925 int error;
2927 error = VOCALL(&fifo_vnode_vops, &ap->a_head);
2928 if (error)
2929 error = hammer2_vop_kqfilter(ap);
2930 return(error);
2931 #endif
2932 return (EOPNOTSUPP);
2936 * VOPS vector
2938 struct vop_ops hammer2_vnode_vops = {
2939 .vop_default = vop_defaultop,
2940 .vop_fsync = hammer2_vop_fsync,
2941 .vop_getpages = vop_stdgetpages,
2942 .vop_putpages = vop_stdputpages,
2943 .vop_access = hammer2_vop_access,
2944 .vop_advlock = hammer2_vop_advlock,
2945 .vop_close = hammer2_vop_close,
2946 .vop_nlink = hammer2_vop_nlink,
2947 .vop_ncreate = hammer2_vop_ncreate,
2948 .vop_nsymlink = hammer2_vop_nsymlink,
2949 .vop_nremove = hammer2_vop_nremove,
2950 .vop_nrmdir = hammer2_vop_nrmdir,
2951 .vop_nrename = hammer2_vop_nrename,
2952 .vop_getattr = hammer2_vop_getattr,
2953 .vop_getattr_lite = hammer2_vop_getattr_lite,
2954 .vop_setattr = hammer2_vop_setattr,
2955 .vop_readdir = hammer2_vop_readdir,
2956 .vop_readlink = hammer2_vop_readlink,
2957 .vop_read = hammer2_vop_read,
2958 .vop_write = hammer2_vop_write,
2959 .vop_open = hammer2_vop_open,
2960 .vop_inactive = hammer2_vop_inactive,
2961 .vop_reclaim = hammer2_vop_reclaim,
2962 .vop_nresolve = hammer2_vop_nresolve,
2963 .vop_nlookupdotdot = hammer2_vop_nlookupdotdot,
2964 .vop_nmkdir = hammer2_vop_nmkdir,
2965 .vop_nmknod = hammer2_vop_nmknod,
2966 .vop_ioctl = hammer2_vop_ioctl,
2967 .vop_mountctl = hammer2_vop_mountctl,
2968 .vop_bmap = hammer2_vop_bmap,
2969 .vop_strategy = hammer2_vop_strategy,
2970 .vop_kqfilter = hammer2_vop_kqfilter
2973 struct vop_ops hammer2_spec_vops = {
2974 .vop_default = vop_defaultop,
2975 .vop_fsync = hammer2_vop_fsync,
2976 .vop_read = vop_stdnoread,
2977 .vop_write = vop_stdnowrite,
2978 .vop_access = hammer2_vop_access,
2979 .vop_close = hammer2_vop_close,
2980 .vop_markatime = hammer2_vop_markatime,
2981 .vop_getattr = hammer2_vop_getattr,
2982 .vop_inactive = hammer2_vop_inactive,
2983 .vop_reclaim = hammer2_vop_reclaim,
2984 .vop_setattr = hammer2_vop_setattr
2987 struct vop_ops hammer2_fifo_vops = {
2988 .vop_default = fifo_vnoperate,
2989 .vop_fsync = hammer2_vop_fsync,
2990 #if 0
2991 .vop_read = hammer2_vop_fiforead,
2992 .vop_write = hammer2_vop_fifowrite,
2993 #endif
2994 .vop_access = hammer2_vop_access,
2995 #if 0
2996 .vop_close = hammer2_vop_fifoclose,
2997 #endif
2998 .vop_markatime = hammer2_vop_markatime,
2999 .vop_getattr = hammer2_vop_getattr,
3000 .vop_inactive = hammer2_vop_inactive,
3001 .vop_reclaim = hammer2_vop_reclaim,
3002 .vop_setattr = hammer2_vop_setattr,
3003 .vop_kqfilter = hammer2_vop_fifokqfilter