8e583a8692b6cc6fe2f64ac35af3df80afbfb41d
[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 /*
70  * HAMMER2 vfs operations.
71  */
72 static struct vfsops hammer2_vfsops = {
73         .vfs_init       = hammer2_init,
74         .vfs_sync       = hammer2_sync,
75         .vfs_mount      = hammer2_mount,
76         .vfs_unmount    = hammer2_unmount,
77         .vfs_root       = hammer2_root,
78         .vfs_statfs     = hammer2_statfs,
79         .vfs_statvfs    = hammer2_statvfs,
80         .vfs_vget       = hammer2_vget,
81         .vfs_vptofh     = hammer2_vptofh,
82         .vfs_fhtovp     = hammer2_fhtovp,
83         .vfs_checkexp   = hammer2_checkexp
84 };
85
86 MALLOC_DEFINE(M_HAMMER2, "HAMMER2-mount", "");
87
88 VFS_SET(hammer2_vfsops, hammer2, 0);
89 MODULE_VERSION(hammer2, 1);
90
91 static
92 int
93 hammer2_init(struct vfsconf *conf)
94 {
95         int error;
96
97         error = 0;
98
99         if (HAMMER2_BLOCKREF_BYTES != sizeof(struct hammer2_blockref))
100                 error = EINVAL;
101         if (HAMMER2_INODE_BYTES != sizeof(struct hammer2_inode_data))
102                 error = EINVAL;
103         if (HAMMER2_ALLOCREF_BYTES != sizeof(struct hammer2_allocref))
104                 error = EINVAL;
105         if (HAMMER2_VOLUME_BYTES != sizeof(struct hammer2_volume_data))
106                 error = EINVAL;
107
108         if (error)
109                 kprintf("HAMMER2 structure size mismatch; cannot continue.\n");
110
111         return (error);
112 }
113
114 /*
115  * Mount or remount HAMMER2 fileystem from physical media
116  *
117  *      mountroot
118  *              mp              mount point structure
119  *              path            NULL
120  *              data            <unused>
121  *              cred            <unused>
122  *
123  *      mount
124  *              mp              mount point structure
125  *              path            path to mount point
126  *              data            pointer to argument structure in user space
127  *                      volume  volume path (device@LABEL form)
128  *                      hflags  user mount flags
129  *              cred            user credentials
130  *
131  * RETURNS:     0       Success
132  *              !0      error number
133  */
134 static
135 int
136 hammer2_mount(struct mount *mp, char *path, caddr_t data,
137               struct ucred *cred)
138 {
139         struct hammer2_mount_info info;
140         hammer2_mount_t *hmp;
141         struct vnode *devvp;
142         struct hammer2_volume_data *vd;
143         struct nlookupdata nd;
144         struct buf *bp;
145         char devstr[MNAMELEN];
146         size_t size;
147         size_t done;
148         char *dev, *label;
149         int ronly = 1;
150         int error;
151
152         hmp = NULL;
153         dev = label = NULL;
154         devvp = NULL;
155         vd = NULL;
156
157         kprintf("hammer2_mount\n");
158
159         if (path == NULL) {
160                 /*
161                  * Root mount
162                  */
163                 return (EOPNOTSUPP);
164         } else {
165                 /*
166                  * Non-root mount or updating a mount
167                  */
168                 error = copyin(data, &info, sizeof(info));
169                 if (error)
170                         return (error);
171
172                 error = copyinstr(info.volume, devstr, MNAMELEN - 1, &done);
173                 if (error)
174                         return (error);
175
176                 /* Extract device and label */
177                 dev = devstr;
178                 label = strchr(devstr, '@');
179                 if (label == NULL ||
180                     ((label + 1) - dev) > done)
181                         return (EINVAL);
182                 *label = '\0';
183                 label++;
184                 if (*label == '\0')
185                         return (EINVAL);
186
187                 if (mp->mnt_flag & MNT_UPDATE) {
188                         /* Update mount */
189                         /* HAMMER2 implements NFS export via mountctl */
190                         hmp = MPTOH2(mp);
191                         devvp = hmp->devvp;
192                         error = hammer2_remount(mp, path, devvp, cred);
193                         return error;
194                 }
195         }
196
197         /*
198          * New non-root mount
199          */
200         /* Lookup name and verify it refers to a block device */
201         error = nlookup_init(&nd, dev, UIO_SYSSPACE, NLC_FOLLOW);
202         if (error == 0)
203                 error = nlookup(&nd);
204         if (error == 0)
205                 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
206         nlookup_done(&nd);
207
208         if (error == 0) {
209                 if (vn_isdisk(devvp, &error))
210                         error = vfs_mountedon(devvp);
211         }
212         if (error == 0 && vcount(devvp) > 0)
213                 error = EBUSY;
214
215         /*
216          * Now open the device
217          */
218         if (error == 0) {
219                 ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
220                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
221                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
222                 if (error == 0) {
223                         error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE,
224                                          FSCRED, NULL);
225                 }
226                 vn_unlock(devvp);
227         }
228         if (error && devvp) {
229                 vrele(devvp);
230                 devvp = NULL;
231         }
232         if (error)
233                 return error;
234
235         /* Read the volume header */
236         /* XXX: Read four volhdrs, find one w/ highest TID & CRC */
237         error = bread(devvp, 0, HAMMER2_PBUFSIZE, &bp);
238         vd = (struct hammer2_volume_data *) bp->b_data;
239         if (error ||
240             (vd->magic != HAMMER2_VOLUME_ID_HBO)) {
241                 brelse(bp);
242                 vrele(devvp);
243                 VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE);
244                 return (EINVAL);
245         }
246
247         /*
248          * Block device opened successfully, finish initializing the
249          * mount structure.
250          *
251          * From this point on we have to call hammer2_unmount() on failure.
252          */
253         hmp = kmalloc(sizeof(*hmp), M_HAMMER2, M_WAITOK | M_ZERO);
254         mp->mnt_data = (qaddr_t)hmp;
255         hmp->mp = mp;
256         hmp->ronly = ronly;
257         hmp->devvp = devvp;
258         lockinit(&hmp->lk, "h2mp", 0, 0);
259         kmalloc_create(&hmp->inodes, "HAMMER2-inodes");
260         kmalloc_create(&hmp->ipstacks, "HAMMER2-ipstacks");
261         
262         bcopy(bp->b_data, &hmp->voldata, sizeof(struct hammer2_volume_data));
263         brelse(bp);
264         bp = NULL;
265
266         mp->mnt_flag = MNT_LOCAL;
267
268         /*
269          * Filesystem subroutines are self-synchronized
270          */
271         mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;
272
273         /* Setup root inode */
274         hmp->iroot = hammer2_alloci(hmp);
275         hmp->iroot->type = HAMMER2_INODE_TYPE_DIR | HAMMER2_INODE_TYPE_ROOT;
276         hmp->iroot->data.inum = 1;
277
278         vfs_getnewfsid(mp);
279         vfs_add_vnodeops(mp, &hammer2_vnode_vops, &mp->mnt_vn_norm_ops);
280         vfs_add_vnodeops(mp, &hammer2_spec_vops, &mp->mnt_vn_spec_ops);
281         vfs_add_vnodeops(mp, &hammer2_fifo_vops, &mp->mnt_vn_fifo_ops);
282
283         copystr(info.volume, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
284         bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
285         bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
286         copyinstr(path, mp->mnt_stat.f_mntonname,
287                   sizeof(mp->mnt_stat.f_mntonname) - 1,
288                   &size);
289
290         hammer2_statfs(mp, &mp->mnt_stat, cred);
291
292         hammer2_inode_unlock_ex(hmp->iroot);
293
294         return 0;
295 }
296
297 static
298 int
299 hammer2_remount(struct mount *mp, char *path, struct vnode *devvp,
300                 struct ucred *cred)
301 {
302         return (0);
303 }
304
305 static
306 int
307 hammer2_unmount(struct mount *mp, int mntflags)
308 {
309         hammer2_mount_t *hmp;
310         int flags;
311         int error;
312         int ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
313         struct vnode *devvp;
314
315         kprintf("hammer2_unmount\n");
316
317         hmp = MPTOH2(mp);
318         flags = 0;
319
320         if (mntflags & MNT_FORCE)
321                 flags |= FORCECLOSE;
322
323         hammer2_mount_exlock(hmp);
324
325         error = vflush(mp, 0, flags);
326
327         /*
328          * Work to do:
329          *      1) Wait on the flusher having no work; heat up if needed
330          *      2) Scan inode RB tree till all the inodes are free
331          *      3) Destroy the kmalloc inode zone
332          *      4) Free the mount point
333          */
334
335         if (hmp->iroot) {
336                 hammer2_inode_lock_ex(hmp->iroot);
337                 hammer2_freei(hmp->iroot);
338                 hmp->iroot = NULL;
339         }
340         if ((devvp = hmp->devvp) != NULL) {
341                 vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
342                 hmp->devvp = NULL;
343                 VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE));
344                 vrele(devvp);
345                 devvp = NULL;
346         }
347
348         kmalloc_destroy(&hmp->inodes);
349         kmalloc_destroy(&hmp->ipstacks);
350
351         hammer2_mount_unlock(hmp);
352
353         mp->mnt_data = NULL;
354         hmp->mp = NULL;
355         kfree(hmp, M_HAMMER2);
356
357         return (error);
358 }
359
360 static
361 int
362 hammer2_vget(struct mount *mp, struct vnode *dvp,
363              ino_t ino, struct vnode **vpp)
364 {
365         kprintf("hammer2_vget\n");
366         return (EOPNOTSUPP);
367 }
368
369 static
370 int
371 hammer2_root(struct mount *mp, struct vnode **vpp)
372 {
373         hammer2_mount_t *hmp;
374         int error;
375         struct vnode *vp;
376
377         kprintf("hammer2_root\n");
378
379         hmp = MPTOH2(mp);
380         hammer2_mount_exlock(hmp);
381         if (hmp->iroot == NULL) {
382                 *vpp = NULL;
383                 error = EINVAL;
384         } else {
385                 vp = hammer2_igetv(hmp->iroot, &error);
386                 *vpp = vp;
387                 if (vp == NULL)
388                         kprintf("vnodefail\n");
389         }
390         hammer2_mount_unlock(hmp);
391
392         return (error);
393 }
394
395 static
396 int
397 hammer2_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
398 {
399         hammer2_mount_t *hmp;
400
401         kprintf("hammer2_statfs\n");
402
403         hmp = MPTOH2(mp);
404
405         sbp->f_iosize = PAGE_SIZE;
406         sbp->f_bsize = PAGE_SIZE;
407
408         sbp->f_blocks = 10;
409         sbp->f_bavail = 10;
410         sbp->f_bfree = 10;
411
412         sbp->f_files = 10;
413         sbp->f_ffree = 10;
414         sbp->f_owner = 0;
415
416         return (0);
417 }
418
419 static
420 int
421 hammer2_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
422 {
423         kprintf("hammer2_statvfs\n");
424         return (EOPNOTSUPP);
425 }
426
427 /*
428  * Sync the entire filesystem; this is called from the filesystem syncer
429  * process periodically and whenever a user calls sync(1) on the hammer
430  * mountpoint.
431  *
432  * Currently is actually called from the syncer! \o/
433  *
434  * This task will have to snapshot the state of the dirty inode chain.
435  * From that, it will have to make sure all of the inodes on the dirty
436  * chain have IO initiated. We make sure that io is initiated for the root
437  * block.
438  *
439  * If waitfor is set, we wait for media to acknowledge the new rootblock.
440  *
441  * THINKS: side A vs side B, to have sync not stall all I/O?
442  */
443 static
444 int
445 hammer2_sync(struct mount *mp, int waitfor)
446 {
447 #if 0
448         hammer2_mount_t *hmp;
449         hammer2_inode_t *ip;
450 #endif
451
452         kprintf("hammer2_sync \n");
453
454 #if 0
455         hmp = MPTOH2(mp);
456 #endif
457
458         return (0);
459 }
460
461 static
462 int
463 hammer2_vptofh(struct vnode *vp, struct fid *fhp)
464 {
465         return (0);
466 }
467
468 static
469 int
470 hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
471                struct fid *fhp, struct vnode **vpp)
472 {
473         return (0);
474 }
475
476 static
477 int
478 hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
479                  int *exflagsp, struct ucred **credanonp)
480 {
481         return (0);
482 }