hammer2 - wire in statfs/statvfs, add the chain structure, etc
[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         struct vnode *devvp;
144         struct nlookupdata nd;
145         char devstr[MNAMELEN];
146         size_t size;
147         size_t done;
148         char *dev;
149         char *label;
150         int ronly = 1;
151         int error;
152
153         hmp = NULL;
154         dev = NULL;
155         label = NULL;
156         devvp = NULL;
157
158         kprintf("hammer2_mount\n");
159
160         if (path == NULL) {
161                 /*
162                  * Root mount
163                  */
164                 return (EOPNOTSUPP);
165         } else {
166                 /*
167                  * Non-root mount or updating a mount
168                  */
169                 error = copyin(data, &info, sizeof(info));
170                 if (error)
171                         return (error);
172
173                 error = copyinstr(info.volume, devstr, MNAMELEN - 1, &done);
174                 if (error)
175                         return (error);
176
177                 /* Extract device and label */
178                 dev = devstr;
179                 label = strchr(devstr, '@');
180                 if (label == NULL ||
181                     ((label + 1) - dev) > done) {
182                         return (EINVAL);
183                 }
184                 *label = '\0';
185                 label++;
186                 if (*label == '\0')
187                         return (EINVAL);
188
189                 if (mp->mnt_flag & MNT_UPDATE) {
190                         /* Update mount */
191                         /* HAMMER2 implements NFS export via mountctl */
192                         hmp = MPTOH2(mp);
193                         devvp = hmp->devvp;
194                         error = hammer2_remount(mp, path, devvp, cred);
195                         return error;
196                 }
197         }
198
199         /*
200          * New non-root mount
201          */
202         /* Lookup name and verify it refers to a block device */
203         error = nlookup_init(&nd, dev, UIO_SYSSPACE, NLC_FOLLOW);
204         if (error == 0)
205                 error = nlookup(&nd);
206         if (error == 0)
207                 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
208         nlookup_done(&nd);
209
210         if (error == 0) {
211                 if (vn_isdisk(devvp, &error))
212                         error = vfs_mountedon(devvp);
213         }
214         if (error == 0 && vcount(devvp) > 0)
215                 error = EBUSY;
216
217         /*
218          * Now open the device
219          */
220         if (error == 0) {
221                 ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
222                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
223                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
224                 if (error == 0) {
225                         error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE,
226                                          FSCRED, NULL);
227                 }
228                 vn_unlock(devvp);
229         }
230         if (error && devvp) {
231                 vrele(devvp);
232                 devvp = NULL;
233         }
234         if (error)
235                 return error;
236
237         /*
238          * Block device opened successfully, finish initializing the
239          * mount structure.
240          *
241          * From this point on we have to call hammer2_unmount() on failure.
242          */
243         hmp = kmalloc(sizeof(*hmp), M_HAMMER2, M_WAITOK | M_ZERO);
244         mp->mnt_data = (qaddr_t)hmp;
245         hmp->mp = mp;
246         hmp->ronly = ronly;
247         hmp->devvp = devvp;
248         lockinit(&hmp->lk, "h2mp", 0, 0);
249         kmalloc_create(&hmp->inodes, "HAMMER2-inodes");
250         kmalloc_create(&hmp->ipstacks, "HAMMER2-ipstacks");
251         
252         mp->mnt_flag = MNT_LOCAL;
253         mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;   /* all entry pts are SMP */
254
255         /*
256          * Install the volume header
257          */
258         error = hammer2_install_volume_header(hmp);
259         if (error) {
260                 hammer2_unmount(mp, MNT_FORCE);
261                 return error;
262         }
263
264         /*
265          * required mount structure initializations
266          */
267         mp->mnt_stat.f_iosize = HAMMER2_PBUFSIZE;
268         mp->mnt_stat.f_bsize = HAMMER2_PBUFSIZE;
269
270         mp->mnt_vstat.f_frsize = HAMMER2_PBUFSIZE;
271         mp->mnt_vstat.f_bsize = HAMMER2_PBUFSIZE;
272
273         /*
274          * Locate the root inode.  The volume header points to the
275          * super-root directory.
276          */
277         /* XXX */
278
279
280         /* Setup root inode */
281         hmp->iroot = hammer2_alloci(hmp);
282         hmp->iroot->type = HAMMER2_INODE_TYPE_DIR | HAMMER2_INODE_TYPE_ROOT;
283         hmp->iroot->data.inum = 1;
284
285         vfs_getnewfsid(mp);
286         vfs_add_vnodeops(mp, &hammer2_vnode_vops, &mp->mnt_vn_norm_ops);
287         vfs_add_vnodeops(mp, &hammer2_spec_vops, &mp->mnt_vn_spec_ops);
288         vfs_add_vnodeops(mp, &hammer2_fifo_vops, &mp->mnt_vn_fifo_ops);
289
290         copystr(info.volume, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
291         bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
292         bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
293         copyinstr(path, mp->mnt_stat.f_mntonname,
294                   sizeof(mp->mnt_stat.f_mntonname) - 1,
295                   &size);
296
297         hammer2_statfs(mp, &mp->mnt_stat, cred);
298
299         hammer2_inode_unlock_ex(hmp->iroot);
300
301         return 0;
302 }
303
304 static
305 int
306 hammer2_remount(struct mount *mp, char *path, struct vnode *devvp,
307                 struct ucred *cred)
308 {
309         return (0);
310 }
311
312 static
313 int
314 hammer2_unmount(struct mount *mp, int mntflags)
315 {
316         hammer2_mount_t *hmp;
317         int flags;
318         int error = 0;
319         int ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
320         struct vnode *devvp;
321
322         kprintf("hammer2_unmount\n");
323
324         hmp = MPTOH2(mp);
325         flags = 0;
326
327         if (mntflags & MNT_FORCE)
328                 flags |= FORCECLOSE;
329
330         hammer2_mount_exlock(hmp);
331
332         /*
333          * If mount initialization proceeded far enough we must flush
334          * its vnodes.
335          */
336         if (hmp->iroot)
337                 error = vflush(mp, 0, flags);
338
339         if (error)
340                 return error;
341
342         /*
343          * Work to do:
344          *      1) Wait on the flusher having no work; heat up if needed
345          *      2) Scan inode RB tree till all the inodes are free
346          *      3) Destroy the kmalloc inode zone
347          *      4) Free the mount point
348          */
349
350         if (hmp->iroot) {
351                 hammer2_inode_lock_ex(hmp->iroot);
352                 hammer2_freei(hmp->iroot);
353                 hmp->iroot = NULL;
354         }
355         if ((devvp = hmp->devvp) != NULL) {
356                 vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
357                 hmp->devvp = NULL;
358                 VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE));
359                 vrele(devvp);
360                 devvp = NULL;
361         }
362
363         kmalloc_destroy(&hmp->inodes);
364         kmalloc_destroy(&hmp->ipstacks);
365
366         hammer2_mount_unlock(hmp);
367
368         mp->mnt_data = NULL;
369         hmp->mp = NULL;
370         kfree(hmp, M_HAMMER2);
371
372         return (error);
373 }
374
375 static
376 int
377 hammer2_vget(struct mount *mp, struct vnode *dvp,
378              ino_t ino, struct vnode **vpp)
379 {
380         kprintf("hammer2_vget\n");
381         return (EOPNOTSUPP);
382 }
383
384 static
385 int
386 hammer2_root(struct mount *mp, struct vnode **vpp)
387 {
388         hammer2_mount_t *hmp;
389         int error;
390         struct vnode *vp;
391
392         kprintf("hammer2_root\n");
393
394         hmp = MPTOH2(mp);
395         hammer2_mount_exlock(hmp);
396         if (hmp->iroot == NULL) {
397                 *vpp = NULL;
398                 error = EINVAL;
399         } else {
400                 vp = hammer2_igetv(hmp->iroot, &error);
401                 *vpp = vp;
402                 if (vp == NULL)
403                         kprintf("vnodefail\n");
404         }
405         hammer2_mount_unlock(hmp);
406
407         return (error);
408 }
409
410 static
411 int
412 hammer2_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
413 {
414         hammer2_mount_t *hmp;
415
416         hmp = MPTOH2(mp);
417
418         mp->mnt_stat.f_files = 10;
419         mp->mnt_stat.f_bfree = 10;
420         mp->mnt_stat.f_bavail = mp->mnt_stat.f_bfree;
421
422         *sbp = mp->mnt_stat;
423         return (0);
424 }
425
426 static
427 int
428 hammer2_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
429 {
430         hammer2_mount_t *hmp;
431
432         hmp = MPTOH2(mp);
433
434         mp->mnt_vstat.f_files = 10;
435         mp->mnt_vstat.f_bfree = 10;
436         mp->mnt_vstat.f_bavail = mp->mnt_stat.f_bfree;
437
438         *sbp = mp->mnt_vstat;
439         return (0);
440 }
441
442 /*
443  * Sync the entire filesystem; this is called from the filesystem syncer
444  * process periodically and whenever a user calls sync(1) on the hammer
445  * mountpoint.
446  *
447  * Currently is actually called from the syncer! \o/
448  *
449  * This task will have to snapshot the state of the dirty inode chain.
450  * From that, it will have to make sure all of the inodes on the dirty
451  * chain have IO initiated. We make sure that io is initiated for the root
452  * block.
453  *
454  * If waitfor is set, we wait for media to acknowledge the new rootblock.
455  *
456  * THINKS: side A vs side B, to have sync not stall all I/O?
457  */
458 static
459 int
460 hammer2_sync(struct mount *mp, int waitfor)
461 {
462 #if 0
463         hammer2_mount_t *hmp;
464         hammer2_inode_t *ip;
465 #endif
466
467         kprintf("hammer2_sync \n");
468
469 #if 0
470         hmp = MPTOH2(mp);
471 #endif
472
473         return (0);
474 }
475
476 static
477 int
478 hammer2_vptofh(struct vnode *vp, struct fid *fhp)
479 {
480         return (0);
481 }
482
483 static
484 int
485 hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
486                struct fid *fhp, struct vnode **vpp)
487 {
488         return (0);
489 }
490
491 static
492 int
493 hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
494                  int *exflagsp, struct ucred **credanonp)
495 {
496         return (0);
497 }
498
499 /*
500  * Support code for hammer2_mount().  Read, verify, and install the volume
501  * header into the HMP
502  *
503  * XXX read four volhdrs and use the one with the highest TID whos CRC
504  *     matches.
505  *
506  * XXX check iCRCs.
507  */
508 static
509 int
510 hammer2_install_volume_header(hammer2_mount_t *hmp)
511 {
512         hammer2_volume_data_t *vd;
513         struct buf *bp = NULL;
514         int error;
515
516         error = bread(hmp->devvp, 0, HAMMER2_PBUFSIZE, &bp);
517         if (error)
518                 return error;
519         bcopy(bp->b_data, &hmp->voldata, sizeof(hmp->voldata));
520         brelse(bp);
521         bp = NULL;
522
523         vd = &hmp->voldata;
524         if (vd->magic != HAMMER2_VOLUME_ID_HBO) {
525                 kprintf("hammer_mount: volume header magic is wrong\n");
526                 return EINVAL;
527         }
528         kprintf("hammer_mount: volume header magic good\n");
529         return 0;
530 }