hammer2 - Implement more of the hammer2_chain infrastructure
[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 *parent;
147         hammer2_chain_t *schain;
148         hammer2_chain_t *rchain;
149         char devstr[MNAMELEN];
150         size_t size;
151         size_t done;
152         char *dev;
153         char *label;
154         int ronly = 1;
155         int error;
156
157         hmp = NULL;
158         dev = NULL;
159         label = NULL;
160         devvp = NULL;
161
162         kprintf("hammer2_mount\n");
163
164         if (path == NULL) {
165                 /*
166                  * Root mount
167                  */
168                 return (EOPNOTSUPP);
169         } else {
170                 /*
171                  * Non-root mount or updating a mount
172                  */
173                 error = copyin(data, &info, sizeof(info));
174                 if (error)
175                         return (error);
176
177                 error = copyinstr(info.volume, devstr, MNAMELEN - 1, &done);
178                 if (error)
179                         return (error);
180
181                 /* Extract device and label */
182                 dev = devstr;
183                 label = strchr(devstr, '@');
184                 if (label == NULL ||
185                     ((label + 1) - dev) > done) {
186                         return (EINVAL);
187                 }
188                 *label = '\0';
189                 label++;
190                 if (*label == '\0')
191                         return (EINVAL);
192
193                 if (mp->mnt_flag & MNT_UPDATE) {
194                         /* Update mount */
195                         /* HAMMER2 implements NFS export via mountctl */
196                         hmp = MPTOH2(mp);
197                         devvp = hmp->devvp;
198                         error = hammer2_remount(mp, path, devvp, cred);
199                         return error;
200                 }
201         }
202
203         /*
204          * New non-root mount
205          */
206         /* Lookup name and verify it refers to a block device */
207         error = nlookup_init(&nd, dev, UIO_SYSSPACE, NLC_FOLLOW);
208         if (error == 0)
209                 error = nlookup(&nd);
210         if (error == 0)
211                 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
212         nlookup_done(&nd);
213
214         if (error == 0) {
215                 if (vn_isdisk(devvp, &error))
216                         error = vfs_mountedon(devvp);
217         }
218         if (error == 0 && vcount(devvp) > 0)
219                 error = EBUSY;
220
221         /*
222          * Now open the device
223          */
224         if (error == 0) {
225                 ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
226                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
227                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
228                 if (error == 0) {
229                         error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE,
230                                          FSCRED, NULL);
231                 }
232                 vn_unlock(devvp);
233         }
234         if (error && devvp) {
235                 vrele(devvp);
236                 devvp = NULL;
237         }
238         if (error)
239                 return error;
240
241         /*
242          * Block device opened successfully, finish initializing the
243          * mount structure.
244          *
245          * From this point on we have to call hammer2_unmount() on failure.
246          */
247         hmp = kmalloc(sizeof(*hmp), M_HAMMER2, M_WAITOK | M_ZERO);
248         mp->mnt_data = (qaddr_t)hmp;
249         hmp->mp = mp;
250         hmp->ronly = ronly;
251         hmp->devvp = devvp;
252         kmalloc_create(&hmp->minode, "HAMMER2-inodes");
253         kmalloc_create(&hmp->mchain, "HAMMER2-chains");
254         
255         mp->mnt_flag = MNT_LOCAL;
256         mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;   /* all entry pts are SMP */
257
258         /*
259          * vchain setup. vchain.data is special cased to NULL.  vchain.refs
260          * is initialized and will never drop to 0.
261          */
262         hmp->vchain.bref.type = HAMMER2_BREF_TYPE_VOLUME;
263         hmp->vchain.refs = 1;
264         /* hmp->vchain.data is special cased to NULL */
265         lockinit(&hmp->vchain.lk, "volume", 0, LK_CANRECURSE);
266
267         /*
268          * Install the volume header
269          */
270         error = hammer2_install_volume_header(hmp);
271         if (error) {
272                 hammer2_unmount(mp, MNT_FORCE);
273                 return error;
274         }
275
276         /*
277          * required mount structure initializations
278          */
279         mp->mnt_stat.f_iosize = HAMMER2_PBUFSIZE;
280         mp->mnt_stat.f_bsize = HAMMER2_PBUFSIZE;
281
282         mp->mnt_vstat.f_frsize = HAMMER2_PBUFSIZE;
283         mp->mnt_vstat.f_bsize = HAMMER2_PBUFSIZE;
284
285         /*
286          * First locate the super-root inode, which is key 0 relative to the
287          * volume header's blockset.
288          *
289          * Then locate the root inode by scanning the directory keyspace
290          * represented by the label.
291          */
292         lhc = hammer2_dirhash(label, strlen(label));
293         parent = &hmp->vchain;
294         hammer2_chain_ref(hmp, parent);
295         hammer2_chain_lock(hmp, parent);
296         schain = hammer2_chain_lookup(hmp, &parent,
297                                       HAMMER2_SROOT_KEY, (hammer2_key_t)-1);
298         hammer2_chain_put(hmp, parent);
299         if (schain == NULL) {
300                 kprintf("hammer2_mount: invalid super-root\n");
301                 hammer2_unmount(mp, MNT_FORCE);
302                 return EINVAL;
303         }
304
305         parent = schain;
306         hammer2_chain_ref(hmp, parent);
307         rchain = hammer2_chain_lookup(hmp, &parent,
308                                       lhc, HAMMER2_DIRHASH_HIMASK);
309         while (rchain) {
310                 if (rchain->bref.type == HAMMER2_BREF_TYPE_INODE &&
311                     rchain->u.ip &&
312                     strcmp(label, rchain->data->ipdata.filename) == 0) {
313                         break;
314                 }
315                 rchain = hammer2_chain_next(hmp, &parent, rchain,
316                                             lhc, HAMMER2_DIRHASH_HIMASK);
317         }
318         hammer2_chain_put(hmp, parent);
319         if (rchain == NULL) {
320                 kprintf("hammer2_mount: root label not found\n");
321                 hammer2_chain_drop(hmp, schain);
322                 hammer2_unmount(mp, MNT_FORCE);
323                 return EINVAL;
324         }
325
326         hmp->schain = schain;           /* left held & unlocked */
327         hmp->rchain = rchain;           /* left held & unlocked */
328         hmp->iroot = rchain->u.ip;      /* implied hold from rchain */
329         kprintf("iroot %p\n", rchain->u.ip);
330
331         vfs_getnewfsid(mp);
332         vfs_add_vnodeops(mp, &hammer2_vnode_vops, &mp->mnt_vn_norm_ops);
333         vfs_add_vnodeops(mp, &hammer2_spec_vops, &mp->mnt_vn_spec_ops);
334         vfs_add_vnodeops(mp, &hammer2_fifo_vops, &mp->mnt_vn_fifo_ops);
335
336         copyinstr(info.volume, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
337         bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
338         bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
339         copyinstr(path, mp->mnt_stat.f_mntonname,
340                   sizeof(mp->mnt_stat.f_mntonname) - 1,
341                   &size);
342
343         hammer2_statfs(mp, &mp->mnt_stat, cred);
344
345         return 0;
346 }
347
348 static
349 int
350 hammer2_remount(struct mount *mp, char *path, struct vnode *devvp,
351                 struct ucred *cred)
352 {
353         return (0);
354 }
355
356 static
357 int
358 hammer2_unmount(struct mount *mp, int mntflags)
359 {
360         hammer2_mount_t *hmp;
361         int flags;
362         int error = 0;
363         int ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
364         struct vnode *devvp;
365
366         kprintf("hammer2_unmount\n");
367
368         hmp = MPTOH2(mp);
369         flags = 0;
370
371         if (mntflags & MNT_FORCE)
372                 flags |= FORCECLOSE;
373
374         hammer2_mount_exlock(hmp);
375
376         /*
377          * If mount initialization proceeded far enough we must flush
378          * its vnodes.
379          */
380         kprintf("iroot %p\n", hmp->iroot);
381         if (hmp->iroot)
382                 error = vflush(mp, 0, flags);
383
384         if (error)
385                 return error;
386
387         /*
388          * Work to do:
389          *      1) Wait on the flusher having no work; heat up if needed
390          *      2) Scan inode RB tree till all the inodes are free
391          *      3) Destroy the kmalloc inode zone
392          *      4) Free the mount point
393          */
394         hmp->iroot = NULL;
395         if (hmp->rchain) {
396                 KKASSERT(hmp->rchain->refs == 1);
397                 hammer2_chain_drop(hmp, hmp->rchain);
398                 hmp->rchain = NULL;
399         }
400         if (hmp->schain) {
401                 KKASSERT(hmp->schain->refs == 1);
402                 hammer2_chain_drop(hmp, hmp->schain);
403                 hmp->schain = NULL;
404         }
405         if ((devvp = hmp->devvp) != NULL) {
406                 vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
407                 hmp->devvp = NULL;
408                 VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE));
409                 vrele(devvp);
410                 devvp = NULL;
411         }
412
413         kmalloc_destroy(&hmp->minode);
414         kmalloc_destroy(&hmp->mchain);
415
416         hammer2_mount_unlock(hmp);
417
418         mp->mnt_data = NULL;
419         hmp->mp = NULL;
420         kfree(hmp, M_HAMMER2);
421
422         return (error);
423 }
424
425 static
426 int
427 hammer2_vget(struct mount *mp, struct vnode *dvp,
428              ino_t ino, struct vnode **vpp)
429 {
430         kprintf("hammer2_vget\n");
431         return (EOPNOTSUPP);
432 }
433
434 static
435 int
436 hammer2_root(struct mount *mp, struct vnode **vpp)
437 {
438         hammer2_mount_t *hmp;
439         int error;
440         struct vnode *vp;
441
442         kprintf("hammer2_root\n");
443
444         hmp = MPTOH2(mp);
445         hammer2_mount_exlock(hmp);
446         if (hmp->iroot == NULL) {
447                 *vpp = NULL;
448                 error = EINVAL;
449         } else {
450                 vp = hammer2_igetv(hmp->iroot, &error);
451                 *vpp = vp;
452                 if (vp == NULL)
453                         kprintf("vnodefail\n");
454         }
455         hammer2_mount_unlock(hmp);
456
457         return (error);
458 }
459
460 static
461 int
462 hammer2_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
463 {
464         hammer2_mount_t *hmp;
465
466         hmp = MPTOH2(mp);
467
468         mp->mnt_stat.f_files = 10;
469         mp->mnt_stat.f_bfree = 10;
470         mp->mnt_stat.f_bavail = mp->mnt_stat.f_bfree;
471
472         *sbp = mp->mnt_stat;
473         return (0);
474 }
475
476 static
477 int
478 hammer2_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
479 {
480         hammer2_mount_t *hmp;
481
482         hmp = MPTOH2(mp);
483
484         mp->mnt_vstat.f_files = 10;
485         mp->mnt_vstat.f_bfree = 10;
486         mp->mnt_vstat.f_bavail = mp->mnt_stat.f_bfree;
487
488         *sbp = mp->mnt_vstat;
489         return (0);
490 }
491
492 /*
493  * Sync the entire filesystem; this is called from the filesystem syncer
494  * process periodically and whenever a user calls sync(1) on the hammer
495  * mountpoint.
496  *
497  * Currently is actually called from the syncer! \o/
498  *
499  * This task will have to snapshot the state of the dirty inode chain.
500  * From that, it will have to make sure all of the inodes on the dirty
501  * chain have IO initiated. We make sure that io is initiated for the root
502  * block.
503  *
504  * If waitfor is set, we wait for media to acknowledge the new rootblock.
505  *
506  * THINKS: side A vs side B, to have sync not stall all I/O?
507  */
508 static
509 int
510 hammer2_sync(struct mount *mp, int waitfor)
511 {
512 #if 0
513         hammer2_mount_t *hmp;
514         hammer2_inode_t *ip;
515 #endif
516
517         kprintf("hammer2_sync \n");
518
519 #if 0
520         hmp = MPTOH2(mp);
521 #endif
522
523         return (0);
524 }
525
526 static
527 int
528 hammer2_vptofh(struct vnode *vp, struct fid *fhp)
529 {
530         return (0);
531 }
532
533 static
534 int
535 hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
536                struct fid *fhp, struct vnode **vpp)
537 {
538         return (0);
539 }
540
541 static
542 int
543 hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
544                  int *exflagsp, struct ucred **credanonp)
545 {
546         return (0);
547 }
548
549 /*
550  * Support code for hammer2_mount().  Read, verify, and install the volume
551  * header into the HMP
552  *
553  * XXX read four volhdrs and use the one with the highest TID whos CRC
554  *     matches.
555  *
556  * XXX check iCRCs.
557  *
558  * XXX For filesystems w/ less than 4 volhdrs, make sure to not write to
559  *     nonexistant locations.
560  *
561  * XXX Record selected volhdr and ring updates to each of 4 volhdrs
562  */
563 static
564 int
565 hammer2_install_volume_header(hammer2_mount_t *hmp)
566 {
567         hammer2_volume_data_t *vd;
568         struct buf *bp;
569         hammer2_crc32_t ccrc, crc;
570         int error_reported;
571         int error;
572         int valid;
573         int i;
574
575         error_reported = 0;
576         error = 0;
577         valid = 0;
578         bp = NULL;
579
580         /*
581          * There are up to 4 copies of the volume header (syncs iterate
582          * between them so there is no single master).  We don't trust the
583          * volu_size field so we don't know precisely how large the filesystem
584          * is, so depend on the OS to return an error if we go beyond the
585          * block device's EOF.
586          */
587         for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
588                 error = bread(hmp->devvp, i * HAMMER2_RESERVE_BYTES64, 
589                               HAMMER2_VOLUME_BYTES, &bp);
590                 if (error) {
591                         brelse(bp);
592                         bp = NULL;
593                         continue;
594                 }
595
596                 vd = (struct hammer2_volume_data *)bp->b_data;
597                 if (vd->magic != HAMMER2_VOLUME_ID_HBO) 
598                         continue;
599
600                 crc = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
601                 ccrc = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
602                                       HAMMER2_VOLUME_ICRC0_SIZE);
603                 if (ccrc != crc) {
604                         kprintf("hammer2 volume header crc "
605                                 "mismatch copy #%d\t%08x %08x",
606                                 i, ccrc, crc);
607                         error_reported = 1;
608                         brelse(bp);
609                         bp = NULL;
610                         continue;
611                 }
612                 if (valid == 0 || hmp->voldata.last_tid < vd->last_tid) {
613                         valid = 1;
614                         hmp->voldata = *vd;
615                 }
616                 brelse(bp);
617                 bp = NULL;
618         }
619         if (valid) {
620                 error = 0;
621                 if (error_reported)
622                         kprintf("hammer2: a valid volume header was found\n");
623         } else {
624                 error = EINVAL;
625                 kprintf("hammer2: no valid volume headers found!\n");
626         }
627         return (error);
628 }
629