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