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