Adjust for the DragonFly ELF ABI
[dragonfly.git] / sys / vfs / hammer / hammer_vnops.c
CommitLineData
427e5fc6
MD
1/*
2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
9944ae54 34 * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.19 2008/01/15 06:02:57 dillon Exp $
427e5fc6
MD
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/fcntl.h>
41#include <sys/namecache.h>
42#include <sys/vnode.h>
43#include <sys/lockf.h>
44#include <sys/event.h>
45#include <sys/stat.h>
b3deaf57 46#include <sys/dirent.h>
c0ade690 47#include <vm/vm_extern.h>
7a04d74f 48#include <vfs/fifofs/fifo.h>
427e5fc6
MD
49#include "hammer.h"
50
51/*
52 * USERFS VNOPS
53 */
54/*static int hammer_vop_vnoperate(struct vop_generic_args *);*/
66325755
MD
55static int hammer_vop_fsync(struct vop_fsync_args *);
56static int hammer_vop_read(struct vop_read_args *);
57static int hammer_vop_write(struct vop_write_args *);
58static int hammer_vop_access(struct vop_access_args *);
59static int hammer_vop_advlock(struct vop_advlock_args *);
60static int hammer_vop_close(struct vop_close_args *);
61static int hammer_vop_ncreate(struct vop_ncreate_args *);
62static int hammer_vop_getattr(struct vop_getattr_args *);
63static int hammer_vop_nresolve(struct vop_nresolve_args *);
64static int hammer_vop_nlookupdotdot(struct vop_nlookupdotdot_args *);
65static int hammer_vop_nlink(struct vop_nlink_args *);
66static int hammer_vop_nmkdir(struct vop_nmkdir_args *);
67static int hammer_vop_nmknod(struct vop_nmknod_args *);
68static int hammer_vop_open(struct vop_open_args *);
69static int hammer_vop_pathconf(struct vop_pathconf_args *);
70static int hammer_vop_print(struct vop_print_args *);
71static int hammer_vop_readdir(struct vop_readdir_args *);
72static int hammer_vop_readlink(struct vop_readlink_args *);
73static int hammer_vop_nremove(struct vop_nremove_args *);
74static int hammer_vop_nrename(struct vop_nrename_args *);
75static int hammer_vop_nrmdir(struct vop_nrmdir_args *);
76static int hammer_vop_setattr(struct vop_setattr_args *);
77static int hammer_vop_strategy(struct vop_strategy_args *);
78static int hammer_vop_nsymlink(struct vop_nsymlink_args *);
79static int hammer_vop_nwhiteout(struct vop_nwhiteout_args *);
427e5fc6 80
7a04d74f
MD
81static int hammer_vop_fifoclose (struct vop_close_args *);
82static int hammer_vop_fiforead (struct vop_read_args *);
83static int hammer_vop_fifowrite (struct vop_write_args *);
84
85static int hammer_vop_specclose (struct vop_close_args *);
86static int hammer_vop_specread (struct vop_read_args *);
87static int hammer_vop_specwrite (struct vop_write_args *);
88
427e5fc6
MD
89struct vop_ops hammer_vnode_vops = {
90 .vop_default = vop_defaultop,
91 .vop_fsync = hammer_vop_fsync,
c0ade690
MD
92 .vop_getpages = vop_stdgetpages,
93 .vop_putpages = vop_stdputpages,
427e5fc6
MD
94 .vop_read = hammer_vop_read,
95 .vop_write = hammer_vop_write,
96 .vop_access = hammer_vop_access,
97 .vop_advlock = hammer_vop_advlock,
98 .vop_close = hammer_vop_close,
99 .vop_ncreate = hammer_vop_ncreate,
100 .vop_getattr = hammer_vop_getattr,
101 .vop_inactive = hammer_vop_inactive,
102 .vop_reclaim = hammer_vop_reclaim,
103 .vop_nresolve = hammer_vop_nresolve,
104 .vop_nlookupdotdot = hammer_vop_nlookupdotdot,
105 .vop_nlink = hammer_vop_nlink,
106 .vop_nmkdir = hammer_vop_nmkdir,
107 .vop_nmknod = hammer_vop_nmknod,
108 .vop_open = hammer_vop_open,
109 .vop_pathconf = hammer_vop_pathconf,
110 .vop_print = hammer_vop_print,
111 .vop_readdir = hammer_vop_readdir,
112 .vop_readlink = hammer_vop_readlink,
113 .vop_nremove = hammer_vop_nremove,
114 .vop_nrename = hammer_vop_nrename,
115 .vop_nrmdir = hammer_vop_nrmdir,
116 .vop_setattr = hammer_vop_setattr,
117 .vop_strategy = hammer_vop_strategy,
118 .vop_nsymlink = hammer_vop_nsymlink,
119 .vop_nwhiteout = hammer_vop_nwhiteout
120};
121
7a04d74f
MD
122struct vop_ops hammer_spec_vops = {
123 .vop_default = spec_vnoperate,
124 .vop_fsync = hammer_vop_fsync,
125 .vop_read = hammer_vop_specread,
126 .vop_write = hammer_vop_specwrite,
127 .vop_access = hammer_vop_access,
128 .vop_close = hammer_vop_specclose,
129 .vop_getattr = hammer_vop_getattr,
130 .vop_inactive = hammer_vop_inactive,
131 .vop_reclaim = hammer_vop_reclaim,
132 .vop_setattr = hammer_vop_setattr
133};
134
135struct vop_ops hammer_fifo_vops = {
136 .vop_default = fifo_vnoperate,
137 .vop_fsync = hammer_vop_fsync,
138 .vop_read = hammer_vop_fiforead,
139 .vop_write = hammer_vop_fifowrite,
140 .vop_access = hammer_vop_access,
141 .vop_close = hammer_vop_fifoclose,
142 .vop_getattr = hammer_vop_getattr,
143 .vop_inactive = hammer_vop_inactive,
144 .vop_reclaim = hammer_vop_reclaim,
145 .vop_setattr = hammer_vop_setattr
146};
147
8cd0a023
MD
148static int hammer_dounlink(struct nchandle *nch, struct vnode *dvp,
149 struct ucred *cred, int flags);
150static int hammer_vop_strategy_read(struct vop_strategy_args *ap);
151static int hammer_vop_strategy_write(struct vop_strategy_args *ap);
152
427e5fc6
MD
153#if 0
154static
155int
156hammer_vop_vnoperate(struct vop_generic_args *)
157{
158 return (VOCALL(&hammer_vnode_vops, ap));
159}
160#endif
161
66325755
MD
162/*
163 * hammer_vop_fsync { vp, waitfor }
164 */
427e5fc6
MD
165static
166int
66325755 167hammer_vop_fsync(struct vop_fsync_args *ap)
427e5fc6 168{
c0ade690
MD
169 hammer_inode_t ip;
170 int error;
171
172 ip = VTOI(ap->a_vp);
173 error = hammer_sync_inode(ip, ap->a_waitfor, 0);
174 return (error);
427e5fc6
MD
175}
176
66325755
MD
177/*
178 * hammer_vop_read { vp, uio, ioflag, cred }
179 */
427e5fc6
MD
180static
181int
66325755 182hammer_vop_read(struct vop_read_args *ap)
427e5fc6 183{
66325755 184 struct hammer_transaction trans;
c0ade690 185 hammer_inode_t ip;
66325755
MD
186 off_t offset;
187 struct buf *bp;
188 struct uio *uio;
189 int error;
190 int n;
8cd0a023 191 int seqcount;
66325755
MD
192
193 if (ap->a_vp->v_type != VREG)
194 return (EINVAL);
195 ip = VTOI(ap->a_vp);
196 error = 0;
8cd0a023 197 seqcount = ap->a_ioflag >> 16;
66325755 198
8cd0a023 199 hammer_start_transaction(&trans, ip->hmp);
66325755
MD
200
201 /*
202 * Access the data in HAMMER_BUFSIZE blocks via the buffer cache.
203 */
204 uio = ap->a_uio;
205 while (uio->uio_resid > 0 && uio->uio_offset < ip->ino_rec.ino_size) {
206 offset = uio->uio_offset & HAMMER_BUFMASK;
c0ade690 207#if 0
8cd0a023
MD
208 error = cluster_read(ap->a_vp, ip->ino_rec.ino_size,
209 uio->uio_offset - offset, HAMMER_BUFSIZE,
210 MAXBSIZE, seqcount, &bp);
c0ade690
MD
211#endif
212 error = bread(ap->a_vp, uio->uio_offset - offset,
213 HAMMER_BUFSIZE, &bp);
66325755
MD
214 if (error) {
215 brelse(bp);
216 break;
217 }
c0ade690 218 /* bp->b_flags |= B_CLUSTEROK; temporarily disabled */
66325755
MD
219 n = HAMMER_BUFSIZE - offset;
220 if (n > uio->uio_resid)
221 n = uio->uio_resid;
222 if (n > ip->ino_rec.ino_size - uio->uio_offset)
223 n = (int)(ip->ino_rec.ino_size - uio->uio_offset);
224 error = uiomove((char *)bp->b_data + offset, n, uio);
225 if (error) {
8cd0a023 226 bqrelse(bp);
66325755
MD
227 break;
228 }
d113fda1
MD
229 if ((ip->flags & HAMMER_INODE_RO) == 0) {
230 ip->ino_rec.ino_atime = trans.tid;
231 hammer_modify_inode(&trans, ip, HAMMER_INODE_ITIMES);
232 }
66325755
MD
233 bqrelse(bp);
234 }
235 hammer_commit_transaction(&trans);
236 return (error);
427e5fc6
MD
237}
238
66325755
MD
239/*
240 * hammer_vop_write { vp, uio, ioflag, cred }
241 */
427e5fc6
MD
242static
243int
66325755 244hammer_vop_write(struct vop_write_args *ap)
427e5fc6 245{
66325755
MD
246 struct hammer_transaction trans;
247 struct hammer_inode *ip;
248 struct uio *uio;
249 off_t offset;
250 struct buf *bp;
251 int error;
252 int n;
c0ade690 253 int flags;
66325755
MD
254
255 if (ap->a_vp->v_type != VREG)
256 return (EINVAL);
257 ip = VTOI(ap->a_vp);
258 error = 0;
259
d113fda1
MD
260 if (ip->flags & HAMMER_INODE_RO)
261 return (EROFS);
262
66325755
MD
263 /*
264 * Create a transaction to cover the operations we perform.
265 */
8cd0a023 266 hammer_start_transaction(&trans, ip->hmp);
66325755
MD
267 uio = ap->a_uio;
268
269 /*
270 * Check append mode
271 */
272 if (ap->a_ioflag & IO_APPEND)
273 uio->uio_offset = ip->ino_rec.ino_size;
274
275 /*
276 * Check for illegal write offsets. Valid range is 0...2^63-1
277 */
9c448776
MD
278 if (uio->uio_offset < 0 || uio->uio_offset + uio->uio_resid <= 0) {
279 hammer_commit_transaction(&trans);
66325755 280 return (EFBIG);
9c448776 281 }
66325755
MD
282
283 /*
284 * Access the data in HAMMER_BUFSIZE blocks via the buffer cache.
285 */
286 while (uio->uio_resid > 0) {
287 offset = uio->uio_offset & HAMMER_BUFMASK;
c0ade690
MD
288 if (uio->uio_segflg == UIO_NOCOPY) {
289 /*
290 * Issuing a write with the same data backing the
291 * buffer. Instantiate the buffer to collect the
292 * backing vm pages, then read-in any missing bits.
293 *
294 * This case is used by vop_stdputpages().
295 */
296 bp = getblk(ap->a_vp, uio->uio_offset, HAMMER_BUFSIZE,
055f5ff8 297 GETBLK_BHEAVY, 0);
c0ade690
MD
298 if ((bp->b_flags & B_CACHE) == 0) {
299 bqrelse(bp);
300 error = bread(ap->a_vp,
301 uio->uio_offset - offset,
302 HAMMER_BUFSIZE, &bp);
303 if (error) {
304 brelse(bp);
305 break;
306 }
307 }
308 } else if (offset == 0 && uio->uio_resid >= HAMMER_BUFSIZE) {
309 /*
310 * entirely overwrite the buffer
311 */
66325755 312 bp = getblk(ap->a_vp, uio->uio_offset, HAMMER_BUFSIZE,
055f5ff8 313 GETBLK_BHEAVY, 0);
66325755 314 } else if (offset == 0 && uio->uio_offset >= ip->ino_rec.ino_size) {
c0ade690
MD
315 /*
316 * XXX
317 */
66325755 318 bp = getblk(ap->a_vp, uio->uio_offset, HAMMER_BUFSIZE,
055f5ff8 319 GETBLK_BHEAVY, 0);
66325755
MD
320 vfs_bio_clrbuf(bp);
321 } else {
c0ade690
MD
322 /*
323 * Partial overwrite, read in any missing bits then
324 * replace the portion being written.
325 */
66325755
MD
326 error = bread(ap->a_vp, uio->uio_offset - offset,
327 HAMMER_BUFSIZE, &bp);
328 if (error) {
329 brelse(bp);
330 break;
331 }
055f5ff8 332 bheavy(bp);
66325755
MD
333 }
334 n = HAMMER_BUFSIZE - offset;
335 if (n > uio->uio_resid)
336 n = uio->uio_resid;
337 error = uiomove((char *)bp->b_data + offset, n, uio);
338 if (error) {
339 brelse(bp);
340 break;
341 }
c0ade690 342 /* bp->b_flags |= B_CLUSTEROK; temporarily disabled */
66325755
MD
343 if (ip->ino_rec.ino_size < uio->uio_offset) {
344 ip->ino_rec.ino_size = uio->uio_offset;
d113fda1 345 flags = HAMMER_INODE_RDIRTY;
c0ade690
MD
346 vnode_pager_setsize(ap->a_vp, ip->ino_rec.ino_size);
347 } else {
d113fda1 348 flags = 0;
66325755 349 }
d113fda1 350 ip->ino_rec.ino_mtime = trans.tid;
f3b0f382 351 flags |= HAMMER_INODE_ITIMES | HAMMER_INODE_BUFS;
c0ade690 352 hammer_modify_inode(&trans, ip, flags);
66325755
MD
353 if (ap->a_ioflag & IO_SYNC) {
354 bwrite(bp);
355 } else if (ap->a_ioflag & IO_DIRECT) {
66325755
MD
356 bawrite(bp);
357 } else {
66325755
MD
358 bdwrite(bp);
359 }
360 }
361 if (error)
362 hammer_abort_transaction(&trans);
363 else
364 hammer_commit_transaction(&trans);
365 return (error);
427e5fc6
MD
366}
367
66325755
MD
368/*
369 * hammer_vop_access { vp, mode, cred }
370 */
427e5fc6
MD
371static
372int
66325755 373hammer_vop_access(struct vop_access_args *ap)
427e5fc6 374{
66325755
MD
375 struct hammer_inode *ip = VTOI(ap->a_vp);
376 uid_t uid;
377 gid_t gid;
378 int error;
379
380 uid = hammer_to_unix_xid(&ip->ino_data.uid);
381 gid = hammer_to_unix_xid(&ip->ino_data.gid);
382
383 error = vop_helper_access(ap, uid, gid, ip->ino_data.mode,
384 ip->ino_data.uflags);
385 return (error);
427e5fc6
MD
386}
387
66325755
MD
388/*
389 * hammer_vop_advlock { vp, id, op, fl, flags }
390 */
427e5fc6
MD
391static
392int
66325755 393hammer_vop_advlock(struct vop_advlock_args *ap)
427e5fc6 394{
66325755
MD
395 struct hammer_inode *ip = VTOI(ap->a_vp);
396
397 return (lf_advlock(ap, &ip->advlock, ip->ino_rec.ino_size));
427e5fc6
MD
398}
399
66325755
MD
400/*
401 * hammer_vop_close { vp, fflag }
402 */
427e5fc6
MD
403static
404int
66325755 405hammer_vop_close(struct vop_close_args *ap)
427e5fc6 406{
a89aec1b 407 return (vop_stdclose(ap));
427e5fc6
MD
408}
409
66325755
MD
410/*
411 * hammer_vop_ncreate { nch, dvp, vpp, cred, vap }
412 *
413 * The operating system has already ensured that the directory entry
414 * does not exist and done all appropriate namespace locking.
415 */
427e5fc6
MD
416static
417int
66325755 418hammer_vop_ncreate(struct vop_ncreate_args *ap)
427e5fc6 419{
66325755
MD
420 struct hammer_transaction trans;
421 struct hammer_inode *dip;
422 struct hammer_inode *nip;
423 struct nchandle *nch;
424 int error;
425
426 nch = ap->a_nch;
427 dip = VTOI(ap->a_dvp);
428
d113fda1
MD
429 if (dip->flags & HAMMER_INODE_RO)
430 return (EROFS);
431
66325755
MD
432 /*
433 * Create a transaction to cover the operations we perform.
434 */
8cd0a023 435 hammer_start_transaction(&trans, dip->hmp);
66325755
MD
436
437 /*
438 * Create a new filesystem object of the requested type. The
7a04d74f 439 * returned inode will be referenced but not locked.
66325755 440 */
8cd0a023
MD
441
442 error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred, dip, &nip);
0b075555
MD
443 if (error)
444 kprintf("hammer_create_inode error %d\n", error);
66325755
MD
445 if (error) {
446 hammer_abort_transaction(&trans);
447 *ap->a_vpp = NULL;
448 return (error);
449 }
66325755
MD
450
451 /*
452 * Add the new filesystem object to the directory. This will also
453 * bump the inode's link count.
454 */
a89aec1b 455 error = hammer_ip_add_directory(&trans, dip, nch->ncp, nip);
0b075555
MD
456 if (error)
457 kprintf("hammer_ip_add_directory error %d\n", error);
66325755
MD
458
459 /*
460 * Finish up.
461 */
462 if (error) {
a89aec1b 463 hammer_rel_inode(nip, 0);
66325755
MD
464 hammer_abort_transaction(&trans);
465 *ap->a_vpp = NULL;
466 } else {
467 hammer_commit_transaction(&trans);
468 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
a89aec1b
MD
469 hammer_rel_inode(nip, 0);
470 if (error == 0) {
471 cache_setunresolved(ap->a_nch);
472 cache_setvp(ap->a_nch, *ap->a_vpp);
473 }
66325755
MD
474 }
475 return (error);
427e5fc6
MD
476}
477
66325755
MD
478/*
479 * hammer_vop_getattr { vp, vap }
480 */
427e5fc6
MD
481static
482int
66325755 483hammer_vop_getattr(struct vop_getattr_args *ap)
427e5fc6 484{
66325755
MD
485 struct hammer_inode *ip = VTOI(ap->a_vp);
486 struct vattr *vap = ap->a_vap;
487
488#if 0
489 if (cache_check_fsmid_vp(ap->a_vp, &ip->fsmid) &&
490 (vp->v_mount->mnt_flag & MNT_RDONLY) == 0 &&
7f7c1f84 491 ip->obj_asof == XXX
66325755
MD
492 ) {
493 /* LAZYMOD XXX */
494 }
495 hammer_itimes(ap->a_vp);
496#endif
497
498 vap->va_fsid = ip->hmp->fsid_udev;
499 vap->va_fileid = ip->ino_rec.base.base.obj_id;
500 vap->va_mode = ip->ino_data.mode;
501 vap->va_nlink = ip->ino_rec.ino_nlinks;
502 vap->va_uid = hammer_to_unix_xid(&ip->ino_data.uid);
503 vap->va_gid = hammer_to_unix_xid(&ip->ino_data.gid);
504 vap->va_rmajor = 0;
505 vap->va_rminor = 0;
506 vap->va_size = ip->ino_rec.ino_size;
507 hammer_to_timespec(ip->ino_rec.ino_atime, &vap->va_atime);
508 hammer_to_timespec(ip->ino_rec.ino_mtime, &vap->va_mtime);
509 hammer_to_timespec(ip->ino_data.ctime, &vap->va_ctime);
510 vap->va_flags = ip->ino_data.uflags;
511 vap->va_gen = 1; /* hammer inums are unique for all time */
512 vap->va_blocksize = 32768; /* XXX - extract from root volume */
513 vap->va_bytes = ip->ino_rec.ino_size;
514 vap->va_type = hammer_get_vnode_type(ip->ino_rec.base.base.obj_type);
515 vap->va_filerev = 0; /* XXX */
516 /* mtime uniquely identifies any adjustments made to the file */
517 vap->va_fsmid = ip->ino_rec.ino_mtime;
518 vap->va_uid_uuid = ip->ino_data.uid;
519 vap->va_gid_uuid = ip->ino_data.gid;
520 vap->va_fsid_uuid = ip->hmp->fsid;
521 vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
522 VA_FSID_UUID_VALID;
7a04d74f
MD
523
524 switch (ip->ino_rec.base.base.obj_type) {
525 case HAMMER_OBJTYPE_CDEV:
526 case HAMMER_OBJTYPE_BDEV:
527 vap->va_rmajor = ip->ino_data.rmajor;
528 vap->va_rminor = ip->ino_data.rminor;
529 break;
530 default:
531 break;
532 }
533
66325755 534 return(0);
427e5fc6
MD
535}
536
66325755
MD
537/*
538 * hammer_vop_nresolve { nch, dvp, cred }
539 *
540 * Locate the requested directory entry.
541 */
427e5fc6
MD
542static
543int
66325755 544hammer_vop_nresolve(struct vop_nresolve_args *ap)
427e5fc6 545{
66325755 546 struct namecache *ncp;
7f7c1f84
MD
547 hammer_inode_t dip;
548 hammer_inode_t ip;
549 hammer_tid_t asof;
8cd0a023
MD
550 struct hammer_cursor cursor;
551 union hammer_record_ondisk *rec;
66325755
MD
552 struct vnode *vp;
553 int64_t namekey;
554 int error;
7f7c1f84
MD
555 int i;
556 int nlen;
d113fda1 557 int flags;
7f7c1f84
MD
558
559 /*
560 * Misc initialization, plus handle as-of name extensions. Look for
561 * the '@@' extension. Note that as-of files and directories cannot
562 * be modified.
7f7c1f84
MD
563 */
564 dip = VTOI(ap->a_dvp);
565 ncp = ap->a_nch->ncp;
566 asof = dip->obj_asof;
567 nlen = ncp->nc_nlen;
d113fda1 568 flags = dip->flags;
7f7c1f84
MD
569
570 for (i = 0; i < nlen; ++i) {
571 if (ncp->nc_name[i] == '@' && ncp->nc_name[i+1] == '@') {
d113fda1 572 asof = hammer_str_to_tid(ncp->nc_name + i + 2);
7f7c1f84 573 kprintf("ASOF %016llx\n", asof);
d113fda1 574 flags |= HAMMER_INODE_RO;
7f7c1f84
MD
575 break;
576 }
577 }
578 nlen = i;
66325755 579
d113fda1
MD
580 /*
581 * If there is no path component the time extension is relative to
582 * dip.
583 */
584 if (nlen == 0) {
61aeeb33
MD
585 ip = hammer_get_inode(dip->hmp, &dip->cache[1], dip->obj_id,
586 asof, flags, &error);
d113fda1
MD
587 if (error == 0) {
588 error = hammer_get_vnode(ip, LK_EXCLUSIVE, &vp);
589 hammer_rel_inode(ip, 0);
590 } else {
591 vp = NULL;
592 }
593 if (error == 0) {
594 vn_unlock(vp);
595 cache_setvp(ap->a_nch, vp);
596 vrele(vp);
597 }
598 return(error);
599 }
600
8cd0a023
MD
601 /*
602 * Calculate the namekey and setup the key range for the scan. This
603 * works kinda like a chained hash table where the lower 32 bits
604 * of the namekey synthesize the chain.
605 *
606 * The key range is inclusive of both key_beg and key_end.
607 */
7f7c1f84 608 namekey = hammer_directory_namekey(ncp->nc_name, nlen);
66325755 609
61aeeb33 610 hammer_init_cursor_hmp(&cursor, &dip->cache[0], dip->hmp);
8cd0a023
MD
611 cursor.key_beg.obj_id = dip->obj_id;
612 cursor.key_beg.key = namekey;
7f7c1f84 613 cursor.key_beg.create_tid = asof;
8cd0a023
MD
614 cursor.key_beg.delete_tid = 0;
615 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
616 cursor.key_beg.obj_type = 0;
66325755 617
8cd0a023
MD
618 cursor.key_end = cursor.key_beg;
619 cursor.key_end.key |= 0xFFFFFFFFULL;
d26d0ae9 620 cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
66325755
MD
621
622 /*
8cd0a023 623 * Scan all matching records (the chain), locate the one matching
a89aec1b 624 * the requested path component.
8cd0a023
MD
625 *
626 * The hammer_ip_*() functions merge in-memory records with on-disk
627 * records for the purposes of the search.
66325755 628 */
a89aec1b 629 error = hammer_ip_first(&cursor, dip);
0b075555 630 rec = NULL;
a89aec1b
MD
631 while (error == 0) {
632 error = hammer_ip_resolve_data(&cursor);
633 if (error)
66325755 634 break;
a89aec1b 635 rec = cursor.record;
7f7c1f84
MD
636 if (nlen == rec->entry.base.data_len &&
637 bcmp(ncp->nc_name, cursor.data, nlen) == 0) {
66325755
MD
638 break;
639 }
a89aec1b 640 error = hammer_ip_next(&cursor);
66325755
MD
641 }
642 if (error == 0) {
61aeeb33
MD
643 ip = hammer_get_inode(dip->hmp, &dip->cache[1],
644 rec->entry.obj_id, asof,
d113fda1 645 flags, &error);
7f7c1f84
MD
646 if (error == 0) {
647 error = hammer_get_vnode(ip, LK_EXCLUSIVE, &vp);
648 hammer_rel_inode(ip, 0);
649 } else {
650 vp = NULL;
651 }
66325755
MD
652 if (error == 0) {
653 vn_unlock(vp);
654 cache_setvp(ap->a_nch, vp);
655 vrele(vp);
656 }
657 } else if (error == ENOENT) {
658 cache_setvp(ap->a_nch, NULL);
659 }
8cd0a023 660 hammer_done_cursor(&cursor);
66325755 661 return (error);
427e5fc6
MD
662}
663
66325755
MD
664/*
665 * hammer_vop_nlookupdotdot { dvp, vpp, cred }
666 *
667 * Locate the parent directory of a directory vnode.
668 *
669 * dvp is referenced but not locked. *vpp must be returned referenced and
670 * locked. A parent_obj_id of 0 does not necessarily indicate that we are
671 * at the root, instead it could indicate that the directory we were in was
672 * removed.
673 */
427e5fc6
MD
674static
675int
66325755 676hammer_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
427e5fc6 677{
66325755 678 struct hammer_inode *dip;
d113fda1 679 struct hammer_inode *ip;
66325755 680 u_int64_t parent_obj_id;
d113fda1 681 int error;
66325755
MD
682
683 dip = VTOI(ap->a_dvp);
684 if ((parent_obj_id = dip->ino_data.parent_obj_id) == 0) {
685 *ap->a_vpp = NULL;
686 return ENOENT;
687 }
d113fda1 688
61aeeb33
MD
689 ip = hammer_get_inode(dip->hmp, &dip->cache[1], parent_obj_id,
690 dip->obj_asof, dip->flags, &error);
d113fda1
MD
691 if (ip == NULL) {
692 *ap->a_vpp = NULL;
693 return(error);
694 }
695 error = hammer_get_vnode(ip, LK_EXCLUSIVE, ap->a_vpp);
696 hammer_rel_inode(ip, 0);
697 return (error);
427e5fc6
MD
698}
699
66325755
MD
700/*
701 * hammer_vop_nlink { nch, dvp, vp, cred }
702 */
427e5fc6
MD
703static
704int
66325755 705hammer_vop_nlink(struct vop_nlink_args *ap)
427e5fc6 706{
66325755
MD
707 struct hammer_transaction trans;
708 struct hammer_inode *dip;
709 struct hammer_inode *ip;
710 struct nchandle *nch;
711 int error;
712
713 nch = ap->a_nch;
714 dip = VTOI(ap->a_dvp);
715 ip = VTOI(ap->a_vp);
716
d113fda1
MD
717 if (dip->flags & HAMMER_INODE_RO)
718 return (EROFS);
719 if (ip->flags & HAMMER_INODE_RO)
720 return (EROFS);
721
66325755
MD
722 /*
723 * Create a transaction to cover the operations we perform.
724 */
8cd0a023 725 hammer_start_transaction(&trans, dip->hmp);
66325755
MD
726
727 /*
728 * Add the filesystem object to the directory. Note that neither
729 * dip nor ip are referenced or locked, but their vnodes are
730 * referenced. This function will bump the inode's link count.
731 */
a89aec1b 732 error = hammer_ip_add_directory(&trans, dip, nch->ncp, ip);
66325755
MD
733
734 /*
735 * Finish up.
736 */
737 if (error) {
738 hammer_abort_transaction(&trans);
739 } else {
6b4f890b
MD
740 cache_setunresolved(nch);
741 cache_setvp(nch, ap->a_vp);
66325755
MD
742 hammer_commit_transaction(&trans);
743 }
744 return (error);
427e5fc6
MD
745}
746
66325755
MD
747/*
748 * hammer_vop_nmkdir { nch, dvp, vpp, cred, vap }
749 *
750 * The operating system has already ensured that the directory entry
751 * does not exist and done all appropriate namespace locking.
752 */
427e5fc6
MD
753static
754int
66325755 755hammer_vop_nmkdir(struct vop_nmkdir_args *ap)
427e5fc6 756{
66325755
MD
757 struct hammer_transaction trans;
758 struct hammer_inode *dip;
759 struct hammer_inode *nip;
760 struct nchandle *nch;
761 int error;
762
763 nch = ap->a_nch;
764 dip = VTOI(ap->a_dvp);
765
d113fda1
MD
766 if (dip->flags & HAMMER_INODE_RO)
767 return (EROFS);
768
66325755
MD
769 /*
770 * Create a transaction to cover the operations we perform.
771 */
8cd0a023 772 hammer_start_transaction(&trans, dip->hmp);
66325755
MD
773
774 /*
775 * Create a new filesystem object of the requested type. The
8cd0a023 776 * returned inode will be referenced but not locked.
66325755 777 */
8cd0a023 778 error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred, dip, &nip);
0b075555
MD
779 if (error)
780 kprintf("hammer_mkdir error %d\n", error);
66325755
MD
781 if (error) {
782 hammer_abort_transaction(&trans);
783 *ap->a_vpp = NULL;
784 return (error);
785 }
66325755
MD
786
787 /*
788 * Add the new filesystem object to the directory. This will also
789 * bump the inode's link count.
790 */
a89aec1b 791 error = hammer_ip_add_directory(&trans, dip, nch->ncp, nip);
0b075555
MD
792 if (error)
793 kprintf("hammer_mkdir (add) error %d\n", error);
66325755
MD
794
795 /*
796 * Finish up.
797 */
798 if (error) {
a89aec1b 799 hammer_rel_inode(nip, 0);
66325755
MD
800 hammer_abort_transaction(&trans);
801 *ap->a_vpp = NULL;
802 } else {
803 hammer_commit_transaction(&trans);
804 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
a89aec1b
MD
805 hammer_rel_inode(nip, 0);
806 if (error == 0) {
807 cache_setunresolved(ap->a_nch);
808 cache_setvp(ap->a_nch, *ap->a_vpp);
809 }
66325755
MD
810 }
811 return (error);
427e5fc6
MD
812}
813
66325755
MD
814/*
815 * hammer_vop_nmknod { nch, dvp, vpp, cred, vap }
816 *
817 * The operating system has already ensured that the directory entry
818 * does not exist and done all appropriate namespace locking.
819 */
427e5fc6
MD
820static
821int
66325755 822hammer_vop_nmknod(struct vop_nmknod_args *ap)
427e5fc6 823{
66325755
MD
824 struct hammer_transaction trans;
825 struct hammer_inode *dip;
826 struct hammer_inode *nip;
827 struct nchandle *nch;
828 int error;
829
830 nch = ap->a_nch;
831 dip = VTOI(ap->a_dvp);
832
d113fda1
MD
833 if (dip->flags & HAMMER_INODE_RO)
834 return (EROFS);
835
66325755
MD
836 /*
837 * Create a transaction to cover the operations we perform.
838 */
8cd0a023 839 hammer_start_transaction(&trans, dip->hmp);
66325755
MD
840
841 /*
842 * Create a new filesystem object of the requested type. The
8cd0a023 843 * returned inode will be referenced but not locked.
66325755 844 */
8cd0a023 845 error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred, dip, &nip);
66325755
MD
846 if (error) {
847 hammer_abort_transaction(&trans);
848 *ap->a_vpp = NULL;
849 return (error);
850 }
66325755
MD
851
852 /*
853 * Add the new filesystem object to the directory. This will also
854 * bump the inode's link count.
855 */
a89aec1b 856 error = hammer_ip_add_directory(&trans, dip, nch->ncp, nip);
66325755
MD
857
858 /*
859 * Finish up.
860 */
861 if (error) {
a89aec1b 862 hammer_rel_inode(nip, 0);
66325755
MD
863 hammer_abort_transaction(&trans);
864 *ap->a_vpp = NULL;
865 } else {
866 hammer_commit_transaction(&trans);
867 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
a89aec1b
MD
868 hammer_rel_inode(nip, 0);
869 if (error == 0) {
870 cache_setunresolved(ap->a_nch);
871 cache_setvp(ap->a_nch, *ap->a_vpp);
872 }
66325755
MD
873 }
874 return (error);
427e5fc6
MD
875}
876
66325755
MD
877/*
878 * hammer_vop_open { vp, mode, cred, fp }
879 */
427e5fc6
MD
880static
881int
66325755 882hammer_vop_open(struct vop_open_args *ap)
427e5fc6 883{
d113fda1
MD
884 if ((ap->a_mode & FWRITE) && (VTOI(ap->a_vp)->flags & HAMMER_INODE_RO))
885 return (EROFS);
886
a89aec1b 887 return(vop_stdopen(ap));
427e5fc6
MD
888}
889
66325755
MD
890/*
891 * hammer_vop_pathconf { vp, name, retval }
892 */
427e5fc6
MD
893static
894int
66325755 895hammer_vop_pathconf(struct vop_pathconf_args *ap)
427e5fc6
MD
896{
897 return EOPNOTSUPP;
898}
899
66325755
MD
900/*
901 * hammer_vop_print { vp }
902 */
427e5fc6
MD
903static
904int
66325755 905hammer_vop_print(struct vop_print_args *ap)
427e5fc6
MD
906{
907 return EOPNOTSUPP;
908}
909
66325755 910/*
6b4f890b 911 * hammer_vop_readdir { vp, uio, cred, *eofflag, *ncookies, off_t **cookies }
66325755 912 */
427e5fc6
MD
913static
914int
66325755 915hammer_vop_readdir(struct vop_readdir_args *ap)
427e5fc6 916{
6b4f890b
MD
917 struct hammer_cursor cursor;
918 struct hammer_inode *ip;
919 struct uio *uio;
920 hammer_record_ondisk_t rec;
921 hammer_base_elm_t base;
922 int error;
923 int cookie_index;
924 int ncookies;
925 off_t *cookies;
926 off_t saveoff;
927 int r;
928
929 ip = VTOI(ap->a_vp);
930 uio = ap->a_uio;
b3deaf57
MD
931 saveoff = uio->uio_offset;
932
933 if (ap->a_ncookies) {
934 ncookies = uio->uio_resid / 16 + 1;
935 if (ncookies > 1024)
936 ncookies = 1024;
937 cookies = kmalloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK);
938 cookie_index = 0;
939 } else {
940 ncookies = -1;
941 cookies = NULL;
942 cookie_index = 0;
943 }
944
945 /*
946 * Handle artificial entries
947 */
948 error = 0;
949 if (saveoff == 0) {
950 r = vop_write_dirent(&error, uio, ip->obj_id, DT_DIR, 1, ".");
951 if (r)
952 goto done;
953 if (cookies)
954 cookies[cookie_index] = saveoff;
955 ++saveoff;
956 ++cookie_index;
957 if (cookie_index == ncookies)
958 goto done;
959 }
960 if (saveoff == 1) {
961 if (ip->ino_data.parent_obj_id) {
962 r = vop_write_dirent(&error, uio,
963 ip->ino_data.parent_obj_id,
964 DT_DIR, 2, "..");
965 } else {
966 r = vop_write_dirent(&error, uio,
967 ip->obj_id, DT_DIR, 2, "..");
968 }
969 if (r)
970 goto done;
971 if (cookies)
972 cookies[cookie_index] = saveoff;
973 ++saveoff;
974 ++cookie_index;
975 if (cookie_index == ncookies)
976 goto done;
977 }
6b4f890b
MD
978
979 /*
980 * Key range (begin and end inclusive) to scan. Directory keys
981 * directly translate to a 64 bit 'seek' position.
982 */
61aeeb33 983 hammer_init_cursor_hmp(&cursor, &ip->cache[0], ip->hmp);
6b4f890b
MD
984 cursor.key_beg.obj_id = ip->obj_id;
985 cursor.key_beg.create_tid = ip->obj_asof;
986 cursor.key_beg.delete_tid = 0;
987 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
988 cursor.key_beg.obj_type = 0;
b3deaf57 989 cursor.key_beg.key = saveoff;
6b4f890b
MD
990
991 cursor.key_end = cursor.key_beg;
992 cursor.key_end.key = HAMMER_MAX_KEY;
d26d0ae9 993 cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
6b4f890b 994
6b4f890b
MD
995 error = hammer_ip_first(&cursor, ip);
996
997 while (error == 0) {
998 error = hammer_ip_resolve_data(&cursor);
999 if (error)
1000 break;
1001 rec = cursor.record;
1002 base = &rec->base.base;
1003 saveoff = base->key;
1004
7a04d74f
MD
1005 if (base->obj_id != ip->obj_id)
1006 panic("readdir: bad record at %p", cursor.node);
1007
6b4f890b
MD
1008 r = vop_write_dirent(
1009 &error, uio, rec->entry.obj_id,
6b4f890b 1010 hammer_get_dtype(rec->entry.base.base.obj_type),
7a04d74f 1011 rec->entry.base.data_len,
6b4f890b
MD
1012 (void *)cursor.data);
1013 if (r)
1014 break;
1015 ++saveoff;
1016 if (cookies)
1017 cookies[cookie_index] = base->key;
1018 ++cookie_index;
1019 if (cookie_index == ncookies)
1020 break;
1021 error = hammer_ip_next(&cursor);
1022 }
1023 hammer_done_cursor(&cursor);
1024
b3deaf57 1025done:
6b4f890b
MD
1026 if (ap->a_eofflag)
1027 *ap->a_eofflag = (error == ENOENT);
6b4f890b
MD
1028 uio->uio_offset = saveoff;
1029 if (error && cookie_index == 0) {
b3deaf57
MD
1030 if (error == ENOENT)
1031 error = 0;
6b4f890b
MD
1032 if (cookies) {
1033 kfree(cookies, M_TEMP);
1034 *ap->a_ncookies = 0;
1035 *ap->a_cookies = NULL;
1036 }
1037 } else {
7a04d74f
MD
1038 if (error == ENOENT)
1039 error = 0;
6b4f890b
MD
1040 if (cookies) {
1041 *ap->a_ncookies = cookie_index;
1042 *ap->a_cookies = cookies;
1043 }
1044 }
1045 return(error);
427e5fc6
MD
1046}
1047
66325755
MD
1048/*
1049 * hammer_vop_readlink { vp, uio, cred }
1050 */
427e5fc6
MD
1051static
1052int
66325755 1053hammer_vop_readlink(struct vop_readlink_args *ap)
427e5fc6 1054{
7a04d74f
MD
1055 struct hammer_cursor cursor;
1056 struct hammer_inode *ip;
1057 int error;
1058
1059 ip = VTOI(ap->a_vp);
61aeeb33 1060 hammer_init_cursor_hmp(&cursor, &ip->cache[0], ip->hmp);
7a04d74f
MD
1061
1062 /*
1063 * Key range (begin and end inclusive) to scan. Directory keys
1064 * directly translate to a 64 bit 'seek' position.
1065 */
1066 cursor.key_beg.obj_id = ip->obj_id;
1067 cursor.key_beg.create_tid = ip->obj_asof;
1068 cursor.key_beg.delete_tid = 0;
1069 cursor.key_beg.rec_type = HAMMER_RECTYPE_FIX;
1070 cursor.key_beg.obj_type = 0;
1071 cursor.key_beg.key = HAMMER_FIXKEY_SYMLINK;
1072
1073 error = hammer_ip_lookup(&cursor, ip);
1074 if (error == 0) {
1075 error = hammer_ip_resolve_data(&cursor);
1076 if (error == 0) {
1077 error = uiomove((char *)cursor.data,
1078 cursor.record->generic.base.data_len,
1079 ap->a_uio);
1080 }
1081 }
1082 hammer_done_cursor(&cursor);
1083 return(error);
427e5fc6
MD
1084}
1085
66325755
MD
1086/*
1087 * hammer_vop_nremove { nch, dvp, cred }
1088 */
427e5fc6
MD
1089static
1090int
66325755 1091hammer_vop_nremove(struct vop_nremove_args *ap)
427e5fc6 1092{
8cd0a023 1093 return(hammer_dounlink(ap->a_nch, ap->a_dvp, ap->a_cred, 0));
427e5fc6
MD
1094}
1095
66325755
MD
1096/*
1097 * hammer_vop_nrename { fnch, tnch, fdvp, tdvp, cred }
1098 */
427e5fc6
MD
1099static
1100int
66325755 1101hammer_vop_nrename(struct vop_nrename_args *ap)
427e5fc6 1102{
8cd0a023
MD
1103 struct hammer_transaction trans;
1104 struct namecache *fncp;
1105 struct namecache *tncp;
1106 struct hammer_inode *fdip;
1107 struct hammer_inode *tdip;
1108 struct hammer_inode *ip;
1109 struct hammer_cursor cursor;
1110 union hammer_record_ondisk *rec;
1111 int64_t namekey;
1112 int error;
1113
1114 fdip = VTOI(ap->a_fdvp);
1115 tdip = VTOI(ap->a_tdvp);
1116 fncp = ap->a_fnch->ncp;
1117 tncp = ap->a_tnch->ncp;
b3deaf57
MD
1118 ip = VTOI(fncp->nc_vp);
1119 KKASSERT(ip != NULL);
d113fda1
MD
1120
1121 if (fdip->flags & HAMMER_INODE_RO)
1122 return (EROFS);
1123 if (tdip->flags & HAMMER_INODE_RO)
1124 return (EROFS);
1125 if (ip->flags & HAMMER_INODE_RO)
1126 return (EROFS);
1127
8cd0a023
MD
1128 hammer_start_transaction(&trans, fdip->hmp);
1129
1130 /*
b3deaf57
MD
1131 * Remove tncp from the target directory and then link ip as
1132 * tncp. XXX pass trans to dounlink
8cd0a023 1133 */
b3deaf57
MD
1134 error = hammer_dounlink(ap->a_tnch, ap->a_tdvp, ap->a_cred, 0);
1135 if (error == 0 || error == ENOENT)
1136 error = hammer_ip_add_directory(&trans, tdip, tncp, ip);
1137 if (error)
1138 goto failed; /* XXX */
8cd0a023
MD
1139
1140 /*
1141 * Locate the record in the originating directory and remove it.
1142 *
1143 * Calculate the namekey and setup the key range for the scan. This
1144 * works kinda like a chained hash table where the lower 32 bits
1145 * of the namekey synthesize the chain.
1146 *
1147 * The key range is inclusive of both key_beg and key_end.
1148 */
1149 namekey = hammer_directory_namekey(fncp->nc_name, fncp->nc_nlen);
1150
61aeeb33 1151 hammer_init_cursor_hmp(&cursor, &fdip->cache[0], fdip->hmp);
8cd0a023
MD
1152 cursor.key_beg.obj_id = fdip->obj_id;
1153 cursor.key_beg.key = namekey;
1154 cursor.key_beg.create_tid = fdip->obj_asof;
1155 cursor.key_beg.delete_tid = 0;
1156 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
1157 cursor.key_beg.obj_type = 0;
1158
1159 cursor.key_end = cursor.key_beg;
1160 cursor.key_end.key |= 0xFFFFFFFFULL;
d26d0ae9 1161 cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
8cd0a023
MD
1162
1163 /*
1164 * Scan all matching records (the chain), locate the one matching
a89aec1b 1165 * the requested path component.
8cd0a023
MD
1166 *
1167 * The hammer_ip_*() functions merge in-memory records with on-disk
1168 * records for the purposes of the search.
1169 */
a89aec1b
MD
1170 error = hammer_ip_first(&cursor, fdip);
1171 while (error == 0) {
8cd0a023
MD
1172 if (hammer_ip_resolve_data(&cursor) != 0)
1173 break;
a89aec1b 1174 rec = cursor.record;
8cd0a023
MD
1175 if (fncp->nc_nlen == rec->entry.base.data_len &&
1176 bcmp(fncp->nc_name, cursor.data, fncp->nc_nlen) == 0) {
1177 break;
1178 }
a89aec1b 1179 error = hammer_ip_next(&cursor);
8cd0a023 1180 }
8cd0a023
MD
1181
1182 /*
1183 * If all is ok we have to get the inode so we can adjust nlinks.
1184 */
1185 if (error)
1186 goto done;
a89aec1b 1187 error = hammer_ip_del_directory(&trans, &cursor, fdip, ip);
b3deaf57 1188
9944ae54 1189 if (error == 0)
8cd0a023 1190 cache_rename(ap->a_fnch, ap->a_tnch);
8cd0a023 1191done:
c0ade690 1192 hammer_done_cursor(&cursor);
b3deaf57 1193failed:
8cd0a023
MD
1194 if (error == 0) {
1195 hammer_commit_transaction(&trans);
1196 } else {
1197 hammer_abort_transaction(&trans);
1198 }
8cd0a023 1199 return (error);
427e5fc6
MD
1200}
1201
66325755
MD
1202/*
1203 * hammer_vop_nrmdir { nch, dvp, cred }
1204 */
427e5fc6
MD
1205static
1206int
66325755 1207hammer_vop_nrmdir(struct vop_nrmdir_args *ap)
427e5fc6 1208{
8cd0a023 1209 return(hammer_dounlink(ap->a_nch, ap->a_dvp, ap->a_cred, 0));
427e5fc6
MD
1210}
1211
66325755
MD
1212/*
1213 * hammer_vop_setattr { vp, vap, cred }
1214 */
427e5fc6
MD
1215static
1216int
66325755 1217hammer_vop_setattr(struct vop_setattr_args *ap)
427e5fc6 1218{
8cd0a023 1219 struct hammer_transaction trans;
d26d0ae9 1220 struct hammer_cursor *spike = NULL;
8cd0a023
MD
1221 struct vattr *vap;
1222 struct hammer_inode *ip;
1223 int modflags;
1224 int error;
76376933 1225 int64_t aligned_size;
8cd0a023
MD
1226 u_int32_t flags;
1227 uuid_t uuid;
1228
1229 vap = ap->a_vap;
1230 ip = ap->a_vp->v_data;
1231 modflags = 0;
1232
1233 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
1234 return(EROFS);
d113fda1
MD
1235 if (ip->flags & HAMMER_INODE_RO)
1236 return (EROFS);
8cd0a023
MD
1237
1238 hammer_start_transaction(&trans, ip->hmp);
1239 error = 0;
1240
1241 if (vap->va_flags != VNOVAL) {
1242 flags = ip->ino_data.uflags;
1243 error = vop_helper_setattr_flags(&flags, vap->va_flags,
1244 hammer_to_unix_xid(&ip->ino_data.uid),
1245 ap->a_cred);
1246 if (error == 0) {
1247 if (ip->ino_data.uflags != flags) {
1248 ip->ino_data.uflags = flags;
1249 modflags |= HAMMER_INODE_DDIRTY;
1250 }
1251 if (ip->ino_data.uflags & (IMMUTABLE | APPEND)) {
1252 error = 0;
1253 goto done;
1254 }
1255 }
1256 goto done;
1257 }
1258 if (ip->ino_data.uflags & (IMMUTABLE | APPEND)) {
1259 error = EPERM;
1260 goto done;
1261 }
1262 if (vap->va_uid != (uid_t)VNOVAL) {
1263 hammer_guid_to_uuid(&uuid, vap->va_uid);
6b4f890b 1264 if (bcmp(&uuid, &ip->ino_data.uid, sizeof(uuid)) != 0) {
8cd0a023
MD
1265 ip->ino_data.uid = uuid;
1266 modflags |= HAMMER_INODE_DDIRTY;
1267 }
1268 }
1269 if (vap->va_gid != (uid_t)VNOVAL) {
6b4f890b
MD
1270 hammer_guid_to_uuid(&uuid, vap->va_gid);
1271 if (bcmp(&uuid, &ip->ino_data.gid, sizeof(uuid)) != 0) {
8cd0a023
MD
1272 ip->ino_data.gid = uuid;
1273 modflags |= HAMMER_INODE_DDIRTY;
1274 }
1275 }
d26d0ae9 1276 while (vap->va_size != VNOVAL && ip->ino_rec.ino_size != vap->va_size) {
8cd0a023
MD
1277 switch(ap->a_vp->v_type) {
1278 case VREG:
c0ade690
MD
1279 if (vap->va_size < ip->ino_rec.ino_size) {
1280 vtruncbuf(ap->a_vp, vap->va_size,
1281 HAMMER_BUFSIZE);
1282 } else if (vap->va_size > ip->ino_rec.ino_size) {
1283 vnode_pager_setsize(ap->a_vp, vap->va_size);
1284 }
76376933
MD
1285 aligned_size = (vap->va_size + HAMMER_BUFMASK) &
1286 ~(int64_t)HAMMER_BUFMASK;
1287 error = hammer_ip_delete_range(&trans, ip,
1288 aligned_size,
d26d0ae9
MD
1289 0x7FFFFFFFFFFFFFFFLL,
1290 &spike);
76376933
MD
1291 ip->ino_rec.ino_size = vap->va_size;
1292 modflags |= HAMMER_INODE_RDIRTY;
1293 break;
8cd0a023 1294 case VDATABASE:
a89aec1b 1295 error = hammer_ip_delete_range(&trans, ip,
8cd0a023 1296 vap->va_size,
d26d0ae9
MD
1297 0x7FFFFFFFFFFFFFFFLL,
1298 &spike);
c0ade690
MD
1299 ip->ino_rec.ino_size = vap->va_size;
1300 modflags |= HAMMER_INODE_RDIRTY;
8cd0a023
MD
1301 break;
1302 default:
1303 error = EINVAL;
1304 goto done;
1305 }
d26d0ae9
MD
1306 if (error == ENOSPC) {
1307 error = hammer_spike(&spike);
1308 if (error == 0)
1309 continue;
1310 }
1311 KKASSERT(spike == NULL);
1312 break;
8cd0a023
MD
1313 }
1314 if (vap->va_atime.tv_sec != VNOVAL) {
1315 ip->ino_rec.ino_atime =
1316 hammer_timespec_to_transid(&vap->va_atime);
1317 modflags |= HAMMER_INODE_ITIMES;
1318 }
1319 if (vap->va_mtime.tv_sec != VNOVAL) {
1320 ip->ino_rec.ino_mtime =
1321 hammer_timespec_to_transid(&vap->va_mtime);
1322 modflags |= HAMMER_INODE_ITIMES;
1323 }
1324 if (vap->va_mode != (mode_t)VNOVAL) {
1325 if (ip->ino_data.mode != vap->va_mode) {
1326 ip->ino_data.mode = vap->va_mode;
1327 modflags |= HAMMER_INODE_DDIRTY;
1328 }
1329 }
1330done:
1331 if (error) {
1332 hammer_abort_transaction(&trans);
1333 } else {
c0ade690 1334 hammer_modify_inode(&trans, ip, modflags);
8cd0a023
MD
1335 hammer_commit_transaction(&trans);
1336 }
1337 return (error);
427e5fc6
MD
1338}
1339
66325755
MD
1340/*
1341 * hammer_vop_nsymlink { nch, dvp, vpp, cred, vap, target }
1342 */
427e5fc6
MD
1343static
1344int
66325755 1345hammer_vop_nsymlink(struct vop_nsymlink_args *ap)
427e5fc6 1346{
7a04d74f
MD
1347 struct hammer_transaction trans;
1348 struct hammer_inode *dip;
1349 struct hammer_inode *nip;
1350 struct nchandle *nch;
1351 hammer_record_t record;
1352 int error;
1353 int bytes;
1354
1355 ap->a_vap->va_type = VLNK;
1356
1357 nch = ap->a_nch;
1358 dip = VTOI(ap->a_dvp);
1359
d113fda1
MD
1360 if (dip->flags & HAMMER_INODE_RO)
1361 return (EROFS);
1362
7a04d74f
MD
1363 /*
1364 * Create a transaction to cover the operations we perform.
1365 */
1366 hammer_start_transaction(&trans, dip->hmp);
1367
1368 /*
1369 * Create a new filesystem object of the requested type. The
1370 * returned inode will be referenced but not locked.
1371 */
1372
1373 error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred, dip, &nip);
1374 if (error) {
1375 hammer_abort_transaction(&trans);
1376 *ap->a_vpp = NULL;
1377 return (error);
1378 }
1379
1380 /*
1381 * Add the new filesystem object to the directory. This will also
1382 * bump the inode's link count.
1383 */
1384 error = hammer_ip_add_directory(&trans, dip, nch->ncp, nip);
1385
1386 /*
1387 * Add a record representing the symlink. symlink stores the link
1388 * as pure data, not a string, and is no \0 terminated.
1389 */
1390 if (error == 0) {
1391 record = hammer_alloc_mem_record(nip);
1392 bytes = strlen(ap->a_target);
1393
1394 record->rec.generic.base.base.key = HAMMER_FIXKEY_SYMLINK;
1395 record->rec.generic.base.base.rec_type = HAMMER_RECTYPE_FIX;
1396 record->rec.generic.base.data_len = bytes;
1397 if (bytes <= sizeof(record->rec.generic.filler)) {
1398 record->data = (void *)record->rec.generic.filler;
1399 bcopy(ap->a_target, record->data, bytes);
1400 } else {
1401 record->data = (void *)ap->a_target;
1402 /* will be reallocated by routine below */
1403 }
1404 error = hammer_ip_add_record(&trans, record);
1405 }
1406
1407 /*
1408 * Finish up.
1409 */
1410 if (error) {
1411 hammer_rel_inode(nip, 0);
1412 hammer_abort_transaction(&trans);
1413 *ap->a_vpp = NULL;
1414 } else {
1415 hammer_commit_transaction(&trans);
1416 error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
1417 hammer_rel_inode(nip, 0);
1418 if (error == 0) {
1419 cache_setunresolved(ap->a_nch);
1420 cache_setvp(ap->a_nch, *ap->a_vpp);
1421 }
1422 }
1423 return (error);
427e5fc6
MD
1424}
1425
66325755
MD
1426/*
1427 * hammer_vop_nwhiteout { nch, dvp, cred, flags }
1428 */
427e5fc6
MD
1429static
1430int
66325755 1431hammer_vop_nwhiteout(struct vop_nwhiteout_args *ap)
427e5fc6 1432{
8cd0a023 1433 return(hammer_dounlink(ap->a_nch, ap->a_dvp, ap->a_cred, ap->a_flags));
427e5fc6
MD
1434}
1435
66325755
MD
1436/*
1437 * hammer_vop_strategy { vp, bio }
8cd0a023
MD
1438 *
1439 * Strategy call, used for regular file read & write only. Note that the
1440 * bp may represent a cluster.
1441 *
1442 * To simplify operation and allow better optimizations in the future,
1443 * this code does not make any assumptions with regards to buffer alignment
1444 * or size.
66325755 1445 */
427e5fc6
MD
1446static
1447int
66325755 1448hammer_vop_strategy(struct vop_strategy_args *ap)
427e5fc6 1449{
8cd0a023
MD
1450 struct buf *bp;
1451 int error;
1452
1453 bp = ap->a_bio->bio_buf;
1454
1455 switch(bp->b_cmd) {
1456 case BUF_CMD_READ:
1457 error = hammer_vop_strategy_read(ap);
1458 break;
1459 case BUF_CMD_WRITE:
1460 error = hammer_vop_strategy_write(ap);
1461 break;
1462 default:
1463 error = EINVAL;
1464 break;
1465 }
1466 bp->b_error = error;
1467 if (error)
1468 bp->b_flags |= B_ERROR;
1469 biodone(ap->a_bio);
1470 return (error);
427e5fc6
MD
1471}
1472
8cd0a023
MD
1473/*
1474 * Read from a regular file. Iterate the related records and fill in the
1475 * BIO/BUF. Gaps are zero-filled.
1476 *
1477 * The support code in hammer_object.c should be used to deal with mixed
1478 * in-memory and on-disk records.
1479 *
1480 * XXX atime update
1481 */
1482static
1483int
1484hammer_vop_strategy_read(struct vop_strategy_args *ap)
1485{
1486 struct hammer_inode *ip = ap->a_vp->v_data;
1487 struct hammer_cursor cursor;
1488 hammer_record_ondisk_t rec;
1489 hammer_base_elm_t base;
1490 struct bio *bio;
1491 struct buf *bp;
1492 int64_t rec_offset;
a89aec1b 1493 int64_t ran_end;
195c19a1 1494 int64_t tmp64;
8cd0a023
MD
1495 int error;
1496 int boff;
1497 int roff;
1498 int n;
1499
1500 bio = ap->a_bio;
1501 bp = bio->bio_buf;
1502
61aeeb33 1503 hammer_init_cursor_hmp(&cursor, &ip->cache[0], ip->hmp);
8cd0a023
MD
1504
1505 /*
1506 * Key range (begin and end inclusive) to scan. Note that the key's
c0ade690
MD
1507 * stored in the actual records represent BASE+LEN, not BASE. The
1508 * first record containing bio_offset will have a key > bio_offset.
8cd0a023
MD
1509 */
1510 cursor.key_beg.obj_id = ip->obj_id;
1511 cursor.key_beg.create_tid = ip->obj_asof;
1512 cursor.key_beg.delete_tid = 0;
8cd0a023 1513 cursor.key_beg.obj_type = 0;
c0ade690 1514 cursor.key_beg.key = bio->bio_offset + 1;
8cd0a023
MD
1515
1516 cursor.key_end = cursor.key_beg;
a89aec1b
MD
1517 if (ip->ino_rec.base.base.obj_type == HAMMER_OBJTYPE_DBFILE) {
1518 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
1519 cursor.key_end.rec_type = HAMMER_RECTYPE_DB;
1520 cursor.key_end.key = 0x7FFFFFFFFFFFFFFFLL;
1521 } else {
c0ade690 1522 ran_end = bio->bio_offset + bp->b_bufsize;
a89aec1b
MD
1523 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
1524 cursor.key_end.rec_type = HAMMER_RECTYPE_DATA;
195c19a1
MD
1525 tmp64 = ran_end + MAXPHYS + 1; /* work-around GCC-4 bug */
1526 if (tmp64 < ran_end)
a89aec1b
MD
1527 cursor.key_end.key = 0x7FFFFFFFFFFFFFFFLL;
1528 else
7f7c1f84 1529 cursor.key_end.key = ran_end + MAXPHYS + 1;
a89aec1b 1530 }
d26d0ae9 1531 cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
8cd0a023 1532
a89aec1b 1533 error = hammer_ip_first(&cursor, ip);
8cd0a023
MD
1534 boff = 0;
1535
a89aec1b
MD
1536 while (error == 0) {
1537 error = hammer_ip_resolve_data(&cursor);
1538 if (error)
66325755 1539 break;
a89aec1b 1540 rec = cursor.record;
8cd0a023
MD
1541 base = &rec->base.base;
1542
c0ade690 1543 rec_offset = base->key - rec->data.base.data_len;
8cd0a023 1544
66325755 1545 /*
a89aec1b 1546 * Calculate the gap, if any, and zero-fill it.
66325755 1547 */
8cd0a023
MD
1548 n = (int)(rec_offset - (bio->bio_offset + boff));
1549 if (n > 0) {
a89aec1b
MD
1550 if (n > bp->b_bufsize - boff)
1551 n = bp->b_bufsize - boff;
8cd0a023
MD
1552 kprintf("zfill %d bytes\n", n);
1553 bzero((char *)bp->b_data + boff, n);
1554 boff += n;
1555 n = 0;
66325755 1556 }
8cd0a023
MD
1557
1558 /*
1559 * Calculate the data offset in the record and the number
1560 * of bytes we can copy.
a89aec1b
MD
1561 *
1562 * Note there is a degenerate case here where boff may
1563 * already be at bp->b_bufsize.
8cd0a023
MD
1564 */
1565 roff = -n;
1566 n = rec->data.base.data_len - roff;
1567 KKASSERT(n > 0);
1568 if (n > bp->b_bufsize - boff)
1569 n = bp->b_bufsize - boff;
1570 bcopy((char *)cursor.data + roff, (char *)bp->b_data + boff, n);
1571 boff += n;
1572 if (boff == bp->b_bufsize)
66325755 1573 break;
a89aec1b 1574 error = hammer_ip_next(&cursor);
66325755 1575 }
8cd0a023 1576 hammer_done_cursor(&cursor);
66325755
MD
1577
1578 /*
8cd0a023 1579 * There may have been a gap after the last record
66325755 1580 */
8cd0a023
MD
1581 if (error == ENOENT)
1582 error = 0;
1583 if (error == 0 && boff != bp->b_bufsize) {
7f7c1f84 1584 KKASSERT(boff < bp->b_bufsize);
8cd0a023
MD
1585 bzero((char *)bp->b_data + boff, bp->b_bufsize - boff);
1586 /* boff = bp->b_bufsize; */
1587 }
1588 bp->b_resid = 0;
1589 return(error);
1590}
1591
1592/*
1593 * Write to a regular file. Iterate the related records and mark for
1594 * deletion. If existing edge records (left and right side) overlap our
1595 * write they have to be marked deleted and new records created, usually
1596 * referencing a portion of the original data. Then add a record to
1597 * represent the buffer.
1598 *
1599 * The support code in hammer_object.c should be used to deal with mixed
1600 * in-memory and on-disk records.
1601 */
1602static
1603int
1604hammer_vop_strategy_write(struct vop_strategy_args *ap)
1605{
1606 struct hammer_transaction trans;
d26d0ae9 1607 struct hammer_cursor *spike = NULL;
8cd0a023
MD
1608 hammer_inode_t ip;
1609 struct bio *bio;
1610 struct buf *bp;
1611 int error;
1612
1613 bio = ap->a_bio;
1614 bp = bio->bio_buf;
1615 ip = ap->a_vp->v_data;
d113fda1
MD
1616
1617 if (ip->flags & HAMMER_INODE_RO)
1618 return (EROFS);
1619
8cd0a023
MD
1620 hammer_start_transaction(&trans, ip->hmp);
1621
d26d0ae9 1622retry:
8cd0a023
MD
1623 /*
1624 * Delete any records overlapping our range. This function will
d26d0ae9 1625 * (eventually) properly truncate partial overlaps.
8cd0a023 1626 */
a89aec1b
MD
1627 if (ip->ino_rec.base.base.obj_type == HAMMER_OBJTYPE_DBFILE) {
1628 error = hammer_ip_delete_range(&trans, ip, bio->bio_offset,
d26d0ae9 1629 bio->bio_offset, &spike);
a89aec1b
MD
1630 } else {
1631 error = hammer_ip_delete_range(&trans, ip, bio->bio_offset,
d26d0ae9
MD
1632 bio->bio_offset +
1633 bp->b_bufsize - 1,
1634 &spike);
a89aec1b 1635 }
8cd0a023
MD
1636
1637 /*
1638 * Add a single record to cover the write
1639 */
1640 if (error == 0) {
c0ade690 1641 error = hammer_ip_sync_data(&trans, ip, bio->bio_offset,
d26d0ae9
MD
1642 bp->b_data, bp->b_bufsize,
1643 &spike);
1644 }
1645
1646 /*
1647 * If we ran out of space the spike structure will be filled in
1648 * and we must call hammer_spike with it, then retry.
1649 */
1650 if (error == ENOSPC) {
1651 error = hammer_spike(&spike);
1652 if (error == 0)
1653 goto retry;
66325755 1654 }
d26d0ae9 1655 KKASSERT(spike == NULL);
66325755
MD
1656
1657 /*
8cd0a023 1658 * If an error occured abort the transaction
66325755 1659 */
8cd0a023
MD
1660 if (error) {
1661 /* XXX undo deletion */
1662 hammer_abort_transaction(&trans);
1663 bp->b_resid = bp->b_bufsize;
1664 } else {
1665 hammer_commit_transaction(&trans);
1666 bp->b_resid = 0;
1667 }
1668 return(error);
1669}
1670
1671/*
1672 * dounlink - disconnect a directory entry
1673 *
1674 * XXX whiteout support not really in yet
1675 */
1676static int
1677hammer_dounlink(struct nchandle *nch, struct vnode *dvp, struct ucred *cred,
1678 int flags)
1679{
1680 struct hammer_transaction trans;
1681 struct namecache *ncp;
1682 hammer_inode_t dip;
1683 hammer_inode_t ip;
1684 hammer_record_ondisk_t rec;
1685 struct hammer_cursor cursor;
8cd0a023
MD
1686 int64_t namekey;
1687 int error;
1688
1689 /*
1690 * Calculate the namekey and setup the key range for the scan. This
1691 * works kinda like a chained hash table where the lower 32 bits
1692 * of the namekey synthesize the chain.
1693 *
1694 * The key range is inclusive of both key_beg and key_end.
1695 */
1696 dip = VTOI(dvp);
1697 ncp = nch->ncp;
d113fda1
MD
1698
1699 if (dip->flags & HAMMER_INODE_RO)
1700 return (EROFS);
1701
8cd0a023
MD
1702 namekey = hammer_directory_namekey(ncp->nc_name, ncp->nc_nlen);
1703
61aeeb33 1704 hammer_init_cursor_hmp(&cursor, &dip->cache[0], dip->hmp);
8cd0a023
MD
1705 cursor.key_beg.obj_id = dip->obj_id;
1706 cursor.key_beg.key = namekey;
1707 cursor.key_beg.create_tid = dip->obj_asof;
1708 cursor.key_beg.delete_tid = 0;
1709 cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
1710 cursor.key_beg.obj_type = 0;
1711
1712 cursor.key_end = cursor.key_beg;
1713 cursor.key_end.key |= 0xFFFFFFFFULL;
d26d0ae9 1714 cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
8cd0a023
MD
1715
1716 hammer_start_transaction(&trans, dip->hmp);
1717
1718 /*
1719 * Scan all matching records (the chain), locate the one matching
1720 * the requested path component. info->last_error contains the
1721 * error code on search termination and could be 0, ENOENT, or
1722 * something else.
1723 *
1724 * The hammer_ip_*() functions merge in-memory records with on-disk
1725 * records for the purposes of the search.
1726 */
a89aec1b
MD
1727 error = hammer_ip_first(&cursor, dip);
1728 while (error == 0) {
1729 error = hammer_ip_resolve_data(&cursor);
1730 if (error)
66325755 1731 break;
a89aec1b 1732 rec = cursor.record;
8cd0a023
MD
1733 if (ncp->nc_nlen == rec->entry.base.data_len &&
1734 bcmp(ncp->nc_name, cursor.data, ncp->nc_nlen) == 0) {
66325755
MD
1735 break;
1736 }
a89aec1b 1737 error = hammer_ip_next(&cursor);
66325755 1738 }
8cd0a023
MD
1739
1740 /*
1741 * If all is ok we have to get the inode so we can adjust nlinks.
b3deaf57
MD
1742 *
1743 * If the target is a directory, it must be empty.
8cd0a023 1744 */
66325755 1745 if (error == 0) {
61aeeb33
MD
1746 ip = hammer_get_inode(dip->hmp, &dip->cache[1],
1747 rec->entry.obj_id,
d113fda1 1748 dip->hmp->asof, 0, &error);
b3deaf57
MD
1749 if (error == 0 && ip->ino_rec.base.base.obj_type ==
1750 HAMMER_OBJTYPE_DIRECTORY) {
1751 error = hammer_ip_check_directory_empty(&trans, ip);
1752 }
8cd0a023 1753 if (error == 0)
a89aec1b 1754 error = hammer_ip_del_directory(&trans, &cursor, dip, ip);
8cd0a023
MD
1755 if (error == 0) {
1756 cache_setunresolved(nch);
1757 cache_setvp(nch, NULL);
1758 /* XXX locking */
1759 if (ip->vp)
1760 cache_inval_vp(ip->vp, CINV_DESTROY);
1761 }
a89aec1b 1762 hammer_rel_inode(ip, 0);
66325755 1763 }
9c448776
MD
1764
1765 if (error == 0)
1766 hammer_commit_transaction(&trans);
1767 else
1768 hammer_abort_transaction(&trans);
8cd0a023 1769 hammer_done_cursor(&cursor);
66325755 1770 return (error);
66325755
MD
1771}
1772
7a04d74f
MD
1773/************************************************************************
1774 * FIFO AND SPECFS OPS *
1775 ************************************************************************
1776 *
1777 */
1778
1779static int
1780hammer_vop_fifoclose (struct vop_close_args *ap)
1781{
1782 /* XXX update itimes */
1783 return (VOCALL(&fifo_vnode_vops, &ap->a_head));
1784}
1785
1786static int
1787hammer_vop_fiforead (struct vop_read_args *ap)
1788{
1789 int error;
1790
1791 error = VOCALL(&fifo_vnode_vops, &ap->a_head);
1792 /* XXX update access time */
1793 return (error);
1794}
1795
1796static int
1797hammer_vop_fifowrite (struct vop_write_args *ap)
1798{
1799 int error;
1800
1801 error = VOCALL(&fifo_vnode_vops, &ap->a_head);
1802 /* XXX update access time */
1803 return (error);
1804}
1805
1806static int
1807hammer_vop_specclose (struct vop_close_args *ap)
1808{
1809 /* XXX update itimes */
1810 return (VOCALL(&spec_vnode_vops, &ap->a_head));
1811}
1812
1813static int
1814hammer_vop_specread (struct vop_read_args *ap)
1815{
1816 /* XXX update access time */
1817 return (VOCALL(&spec_vnode_vops, &ap->a_head));
1818}
1819
1820static int
1821hammer_vop_specwrite (struct vop_write_args *ap)
1822{
1823 /* XXX update last change time */
1824 return (VOCALL(&spec_vnode_vops, &ap->a_head));
1825}
1826