hammer2 - Implement most of the hammer2_chain_*() function set
[dragonfly.git] / sys / vfs / hammer2 / hammer2_vfsops.c
1 /*-
2  * Copyright (c) 2011, 2012 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 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/nlookup.h>
38 #include <sys/vnode.h>
39 #include <sys/mount.h>
40 #include <sys/fcntl.h>
41 #include <sys/buf.h>
42 #include <sys/uuid.h>
43 #include <sys/vfsops.h>
44
45 #include "hammer2.h"
46 #include "hammer2_disk.h"
47 #include "hammer2_mount.h"
48
49 static int      hammer2_init(struct vfsconf *conf);
50 static int      hammer2_mount(struct mount *mp, char *path, caddr_t data,
51                               struct ucred *cred);
52 static int      hammer2_remount(struct mount *, char *, struct vnode *,
53                                 struct ucred *);
54 static int      hammer2_unmount(struct mount *mp, int mntflags);
55 static int      hammer2_root(struct mount *mp, struct vnode **vpp);
56 static int      hammer2_statfs(struct mount *mp, struct statfs *sbp,
57                                struct ucred *cred);
58 static int      hammer2_statvfs(struct mount *mp, struct statvfs *sbp,
59                                 struct ucred *cred);
60 static int      hammer2_sync(struct mount *mp, int waitfor);
61 static int      hammer2_vget(struct mount *mp, struct vnode *dvp,
62                              ino_t ino, struct vnode **vpp);
63 static int      hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
64                                struct fid *fhp, struct vnode **vpp);
65 static int      hammer2_vptofh(struct vnode *vp, struct fid *fhp);
66 static int      hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
67                                  int *exflagsp, struct ucred **credanonp);
68
69 static int      hammer2_install_volume_header(hammer2_mount_t *hmp);
70
71 /*
72  * HAMMER2 vfs operations.
73  */
74 static struct vfsops hammer2_vfsops = {
75         .vfs_init       = hammer2_init,
76         .vfs_sync       = hammer2_sync,
77         .vfs_mount      = hammer2_mount,
78         .vfs_unmount    = hammer2_unmount,
79         .vfs_root       = hammer2_root,
80         .vfs_statfs     = hammer2_statfs,
81         .vfs_statvfs    = hammer2_statvfs,
82         .vfs_vget       = hammer2_vget,
83         .vfs_vptofh     = hammer2_vptofh,
84         .vfs_fhtovp     = hammer2_fhtovp,
85         .vfs_checkexp   = hammer2_checkexp
86 };
87
88 MALLOC_DEFINE(M_HAMMER2, "HAMMER2-mount", "");
89
90 VFS_SET(hammer2_vfsops, hammer2, 0);
91 MODULE_VERSION(hammer2, 1);
92
93 static
94 int
95 hammer2_init(struct vfsconf *conf)
96 {
97         int error;
98
99         error = 0;
100
101         if (HAMMER2_BLOCKREF_BYTES != sizeof(struct hammer2_blockref))
102                 error = EINVAL;
103         if (HAMMER2_INODE_BYTES != sizeof(struct hammer2_inode_data))
104                 error = EINVAL;
105         if (HAMMER2_ALLOCREF_BYTES != sizeof(struct hammer2_allocref))
106                 error = EINVAL;
107         if (HAMMER2_VOLUME_BYTES != sizeof(struct hammer2_volume_data))
108                 error = EINVAL;
109
110         if (error)
111                 kprintf("HAMMER2 structure size mismatch; cannot continue.\n");
112
113         return (error);
114 }
115
116 /*
117  * Mount or remount HAMMER2 fileystem from physical media
118  *
119  *      mountroot
120  *              mp              mount point structure
121  *              path            NULL
122  *              data            <unused>
123  *              cred            <unused>
124  *
125  *      mount
126  *              mp              mount point structure
127  *              path            path to mount point
128  *              data            pointer to argument structure in user space
129  *                      volume  volume path (device@LABEL form)
130  *                      hflags  user mount flags
131  *              cred            user credentials
132  *
133  * RETURNS:     0       Success
134  *              !0      error number
135  */
136 static
137 int
138 hammer2_mount(struct mount *mp, char *path, caddr_t data,
139               struct ucred *cred)
140 {
141         struct hammer2_mount_info info;
142         hammer2_mount_t *hmp;
143         hammer2_key_t lhc;
144         struct vnode *devvp;
145         struct nlookupdata nd;
146         hammer2_chain_t *schain;
147         hammer2_chain_t *rchain;
148         char devstr[MNAMELEN];
149         size_t size;
150         size_t done;
151         char *dev;
152         char *label;
153         int ronly = 1;
154         int error;
155
156         hmp = NULL;
157         dev = NULL;
158         label = NULL;
159         devvp = NULL;
160
161         kprintf("hammer2_mount\n");
162
163         if (path == NULL) {
164                 /*
165                  * Root mount
166                  */
167                 return (EOPNOTSUPP);
168         } else {
169                 /*
170                  * Non-root mount or updating a mount
171                  */
172                 error = copyin(data, &info, sizeof(info));
173                 if (error)
174                         return (error);
175
176                 error = copyinstr(info.volume, devstr, MNAMELEN - 1, &done);
177                 if (error)
178                         return (error);
179
180                 /* Extract device and label */
181                 dev = devstr;
182                 label = strchr(devstr, '@');
183                 if (label == NULL ||
184                     ((label + 1) - dev) > done) {
185                         return (EINVAL);
186                 }
187                 *label = '\0';
188                 label++;
189                 if (*label == '\0')
190                         return (EINVAL);
191
192                 if (mp->mnt_flag & MNT_UPDATE) {
193                         /* Update mount */
194                         /* HAMMER2 implements NFS export via mountctl */
195                         hmp = MPTOH2(mp);
196                         devvp = hmp->devvp;
197                         error = hammer2_remount(mp, path, devvp, cred);
198                         return error;
199                 }
200         }
201
202         /*
203          * New non-root mount
204          */
205         /* Lookup name and verify it refers to a block device */
206         error = nlookup_init(&nd, dev, UIO_SYSSPACE, NLC_FOLLOW);
207         if (error == 0)
208                 error = nlookup(&nd);
209         if (error == 0)
210                 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
211         nlookup_done(&nd);
212
213         if (error == 0) {
214                 if (vn_isdisk(devvp, &error))
215                         error = vfs_mountedon(devvp);
216         }
217         if (error == 0 && vcount(devvp) > 0)
218                 error = EBUSY;
219
220         /*
221          * Now open the device
222          */
223         if (error == 0) {
224                 ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
225                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
226                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
227                 if (error == 0) {
228                         error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE,
229                                          FSCRED, NULL);
230                 }
231                 vn_unlock(devvp);
232         }
233         if (error && devvp) {
234                 vrele(devvp);
235                 devvp = NULL;
236         }
237         if (error)
238                 return error;
239
240         /*
241          * Block device opened successfully, finish initializing the
242          * mount structure.
243          *
244          * From this point on we have to call hammer2_unmount() on failure.
245          */
246         hmp = kmalloc(sizeof(*hmp), M_HAMMER2, M_WAITOK | M_ZERO);
247         mp->mnt_data = (qaddr_t)hmp;
248         hmp->mp = mp;
249         hmp->ronly = ronly;
250         hmp->devvp = devvp;
251         lockinit(&hmp->lk, "h2mp", 0, 0);
252         kmalloc_create(&hmp->inodes, "HAMMER2-inodes");
253         kmalloc_create(&hmp->ipstacks, "HAMMER2-ipstacks");
254         
255         mp->mnt_flag = MNT_LOCAL;
256         mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;   /* all entry pts are SMP */
257
258         hmp->vchain.bref.type = HAMMER2_BREF_TYPE_VOLUME;
259
260         /*
261          * Install the volume header
262          */
263         error = hammer2_install_volume_header(hmp);
264         if (error) {
265                 hammer2_unmount(mp, MNT_FORCE);
266                 return error;
267         }
268
269         /*
270          * required mount structure initializations
271          */
272         mp->mnt_stat.f_iosize = HAMMER2_PBUFSIZE;
273         mp->mnt_stat.f_bsize = HAMMER2_PBUFSIZE;
274
275         mp->mnt_vstat.f_frsize = HAMMER2_PBUFSIZE;
276         mp->mnt_vstat.f_bsize = HAMMER2_PBUFSIZE;
277
278         /*
279          * First locate the super-root inode, which is key 0 relative to the
280          * volume header's blockset.
281          *
282          * Then locate the root inode by scanning the directory keyspace
283          * represented by the label.
284          */
285         lhc = hammer2_dirhash(label, strlen(label));
286         schain = hammer2_chain_push(hmp, &hmp->vchain, HAMMER2_SROOT_KEY);
287         if (schain == NULL) {
288                 kprintf("hammer2_mount: invalid super-root\n");
289                 hammer2_unmount(mp, MNT_FORCE);
290                 return EINVAL;
291         }
292         rchain = hammer2_chain_first(hmp, schain, lhc, HAMMER2_DIRHASH_LOMASK);
293         while (rchain) {
294                 if (rchain->bref.type == HAMMER2_BREF_TYPE_INODE &&
295                     rchain->u.ip &&
296                     strcmp(label, rchain->u.ip->data.filename) == 0) {
297                         break;
298                 }
299                 rchain = hammer2_chain_next(hmp, rchain,
300                                             lhc, HAMMER2_DIRHASH_LOMASK);
301         }
302         if (rchain == NULL) {
303                 kprintf("hammer2_mount: root label not found\n");
304                 hammer2_chain_drop(hmp, schain);
305                 hammer2_unmount(mp, MNT_FORCE);
306                 return EINVAL;
307         }
308         hmp->schain = schain;
309         hmp->rchain = rchain;
310         hmp->iroot = rchain->u.ip;
311         hammer2_inode_ref(hmp->iroot);  /* additional ref */
312
313         vfs_getnewfsid(mp);
314         vfs_add_vnodeops(mp, &hammer2_vnode_vops, &mp->mnt_vn_norm_ops);
315         vfs_add_vnodeops(mp, &hammer2_spec_vops, &mp->mnt_vn_spec_ops);
316         vfs_add_vnodeops(mp, &hammer2_fifo_vops, &mp->mnt_vn_fifo_ops);
317
318         copystr(info.volume, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
319         bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
320         bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
321         copyinstr(path, mp->mnt_stat.f_mntonname,
322                   sizeof(mp->mnt_stat.f_mntonname) - 1,
323                   &size);
324
325         hammer2_statfs(mp, &mp->mnt_stat, cred);
326
327         return 0;
328 }
329
330 static
331 int
332 hammer2_remount(struct mount *mp, char *path, struct vnode *devvp,
333                 struct ucred *cred)
334 {
335         return (0);
336 }
337
338 static
339 int
340 hammer2_unmount(struct mount *mp, int mntflags)
341 {
342         hammer2_mount_t *hmp;
343         int flags;
344         int error = 0;
345         int ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
346         struct vnode *devvp;
347
348         kprintf("hammer2_unmount\n");
349
350         hmp = MPTOH2(mp);
351         flags = 0;
352
353         if (mntflags & MNT_FORCE)
354                 flags |= FORCECLOSE;
355
356         hammer2_mount_exlock(hmp);
357
358         /*
359          * If mount initialization proceeded far enough we must flush
360          * its vnodes.
361          */
362         if (hmp->iroot)
363                 error = vflush(mp, 0, flags);
364
365         if (error)
366                 return error;
367
368         /*
369          * Work to do:
370          *      1) Wait on the flusher having no work; heat up if needed
371          *      2) Scan inode RB tree till all the inodes are free
372          *      3) Destroy the kmalloc inode zone
373          *      4) Free the mount point
374          */
375         if (hmp->rchain) {
376                 hammer2_chain_drop(hmp, hmp->rchain);
377                 hmp->rchain = NULL;
378         }
379         if (hmp->schain) {
380                 hammer2_chain_drop(hmp, hmp->schain);
381                 hmp->schain = NULL;
382         }
383         if (hmp->iroot) {
384                 hammer2_inode_drop(hmp->iroot);
385                 hmp->iroot = NULL;
386         }
387         if ((devvp = hmp->devvp) != NULL) {
388                 vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
389                 hmp->devvp = NULL;
390                 VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE));
391                 vrele(devvp);
392                 devvp = NULL;
393         }
394
395         kmalloc_destroy(&hmp->inodes);
396         kmalloc_destroy(&hmp->ipstacks);
397
398         hammer2_mount_unlock(hmp);
399
400         mp->mnt_data = NULL;
401         hmp->mp = NULL;
402         kfree(hmp, M_HAMMER2);
403
404         return (error);
405 }
406
407 static
408 int
409 hammer2_vget(struct mount *mp, struct vnode *dvp,
410              ino_t ino, struct vnode **vpp)
411 {
412         kprintf("hammer2_vget\n");
413         return (EOPNOTSUPP);
414 }
415
416 static
417 int
418 hammer2_root(struct mount *mp, struct vnode **vpp)
419 {
420         hammer2_mount_t *hmp;
421         int error;
422         struct vnode *vp;
423
424         kprintf("hammer2_root\n");
425
426         hmp = MPTOH2(mp);
427         hammer2_mount_exlock(hmp);
428         if (hmp->iroot == NULL) {
429                 *vpp = NULL;
430                 error = EINVAL;
431         } else {
432                 vp = hammer2_igetv(hmp->iroot, &error);
433                 *vpp = vp;
434                 if (vp == NULL)
435                         kprintf("vnodefail\n");
436         }
437         hammer2_mount_unlock(hmp);
438
439         return (error);
440 }
441
442 static
443 int
444 hammer2_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
445 {
446         hammer2_mount_t *hmp;
447
448         hmp = MPTOH2(mp);
449
450         mp->mnt_stat.f_files = 10;
451         mp->mnt_stat.f_bfree = 10;
452         mp->mnt_stat.f_bavail = mp->mnt_stat.f_bfree;
453
454         *sbp = mp->mnt_stat;
455         return (0);
456 }
457
458 static
459 int
460 hammer2_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
461 {
462         hammer2_mount_t *hmp;
463
464         hmp = MPTOH2(mp);
465
466         mp->mnt_vstat.f_files = 10;
467         mp->mnt_vstat.f_bfree = 10;
468         mp->mnt_vstat.f_bavail = mp->mnt_stat.f_bfree;
469
470         *sbp = mp->mnt_vstat;
471         return (0);
472 }
473
474 /*
475  * Sync the entire filesystem; this is called from the filesystem syncer
476  * process periodically and whenever a user calls sync(1) on the hammer
477  * mountpoint.
478  *
479  * Currently is actually called from the syncer! \o/
480  *
481  * This task will have to snapshot the state of the dirty inode chain.
482  * From that, it will have to make sure all of the inodes on the dirty
483  * chain have IO initiated. We make sure that io is initiated for the root
484  * block.
485  *
486  * If waitfor is set, we wait for media to acknowledge the new rootblock.
487  *
488  * THINKS: side A vs side B, to have sync not stall all I/O?
489  */
490 static
491 int
492 hammer2_sync(struct mount *mp, int waitfor)
493 {
494 #if 0
495         hammer2_mount_t *hmp;
496         hammer2_inode_t *ip;
497 #endif
498
499         kprintf("hammer2_sync \n");
500
501 #if 0
502         hmp = MPTOH2(mp);
503 #endif
504
505         return (0);
506 }
507
508 static
509 int
510 hammer2_vptofh(struct vnode *vp, struct fid *fhp)
511 {
512         return (0);
513 }
514
515 static
516 int
517 hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
518                struct fid *fhp, struct vnode **vpp)
519 {
520         return (0);
521 }
522
523 static
524 int
525 hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
526                  int *exflagsp, struct ucred **credanonp)
527 {
528         return (0);
529 }
530
531 /*
532  * Support code for hammer2_mount().  Read, verify, and install the volume
533  * header into the HMP
534  *
535  * XXX read four volhdrs and use the one with the highest TID whos CRC
536  *     matches.
537  *
538  * XXX check iCRCs.
539  *
540  * XXX For filesystems w/ less than 4 volhdrs, make sure to not write to
541  *     nonexistant locations.
542  *
543  * XXX Record selected volhdr and ring updates to each of 4 volhdrs
544  */
545 static
546 int
547 hammer2_install_volume_header(hammer2_mount_t *hmp)
548 {
549         hammer2_volume_data_t *vd;
550         struct buf *bp;
551         hammer2_crc32_t ccrc, crc;
552         int error_reported;
553         int error;
554         int valid;
555         int i;
556
557         error_reported = 0;
558         error = 0;
559         valid = 0;
560         bp = NULL;
561
562         /*
563          * There are up to 4 copies of the volume header (syncs iterate
564          * between them so there is no single master).  We don't trust the
565          * volu_size field so we don't know precisely how large the filesystem
566          * is, so depend on the OS to return an error if we go beyond the
567          * block device's EOF.
568          */
569         for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
570                 error = bread(hmp->devvp, i * HAMMER2_RESERVE_BYTES64, 
571                               HAMMER2_VOLUME_BYTES, &bp);
572                 if (error) {
573                         brelse(bp);
574                         bp = NULL;
575                         continue;
576                 }
577
578                 vd = (struct hammer2_volume_data *)bp->b_data;
579                 if (vd->magic != HAMMER2_VOLUME_ID_HBO) 
580                         continue;
581
582                 crc = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
583                 ccrc = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
584                                       HAMMER2_VOLUME_ICRC0_SIZE);
585                 if (ccrc != crc) {
586                         kprintf("hammer2 volume header crc "
587                                 "mismatch copy #%d\t%08x %08x",
588                                 i, ccrc, crc);
589                         error_reported = 1;
590                         brelse(bp);
591                         bp = NULL;
592                         continue;
593                 }
594                 if (valid == 0 || hmp->voldata.last_tid < vd->last_tid) {
595                         valid = 1;
596                         hmp->voldata = *vd;
597                 }
598                 brelse(bp);
599                 bp = NULL;
600         }
601         if (valid) {
602                 error = 0;
603                 if (error_reported)
604                         kprintf("hammer2: a valid volume header was found\n");
605         } else {
606                 error = EINVAL;
607                 kprintf("hammer2: no valid volume headers found!\n");
608         }
609         return (error);
610 }
611