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