1/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
3 *
4 *  Copyright (C) 2001 Cluster File Systems, Inc. <braam@clusterfs.com>
5 *
6 *   This file is part of InterMezzo, http://www.inter-mezzo.org.
7 *
8 *   InterMezzo is free software; you can redistribute it and/or
9 *   modify it under the terms of version 2 of the GNU General Public
10 *   License as published by the Free Software Foundation.
11 *
12 *   InterMezzo is distributed in the hope that it will be useful,
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *   GNU General Public License for more details.
16 *
17 *   You should have received a copy of the GNU General Public License
18 *   along with InterMezzo; if not, write to the Free Software
19 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 *  Managing filesets
22 *
23 */
24
25#define __NO_VERSION__
26#include <stdarg.h>
27
28#include <asm/bitops.h>
29#include <asm/uaccess.h>
30#include <asm/system.h>
31
32#include <linux/errno.h>
33#include <linux/fs.h>
34#include <linux/ext2_fs.h>
35#include <linux/slab.h>
36#include <linux/vmalloc.h>
37#include <linux/sched.h>
38#include <linux/stat.h>
39#include <linux/string.h>
40#include <linux/locks.h>
41#include <linux/blkdev.h>
42#include <linux/init.h>
43#include <linux/module.h>
44
45#include <linux/intermezzo_fs.h>
46#include <linux/intermezzo_psdev.h>
47
48static inline struct presto_file_set *presto_dentry2fset(struct dentry *dentry)
49{
50        if (presto_d2d(dentry) == NULL) {
51                EXIT;
52                return NULL;
53        }
54        return presto_d2d(dentry)->dd_fset;
55}
56
57/* find the fileset dentry for this dentry */
58struct presto_file_set *presto_fset(struct dentry *de)
59{
60        struct dentry *fsde;
61        ENTRY;
62        if ( !de->d_inode ) {
63                CDEBUG(D_INODE,"presto_fset: warning %*s has NULL inode.\n",
64                de->d_name.len, de->d_name.name);
65        }
66        for (fsde = de;; fsde = fsde->d_parent) {
67                if ( presto_dentry2fset(fsde) ) {
68                        EXIT;
69                        return presto_dentry2fset(fsde);
70                }
71                if (fsde->d_parent == fsde)
72                        break;
73        }
74        EXIT;
75        return NULL;
76}
77
78int presto_get_lastrecno(char *path, off_t *recno)
79{
80        struct nameidata nd;
81        struct presto_file_set *fset;
82        struct dentry *dentry;
83        int error;
84        ENTRY;
85
86        error = presto_walk(path, &nd);
87        if (error) {
88                EXIT;
89                return error;
90        }
91
92        dentry = nd.dentry;
93
94        error = -ENXIO;
95        if ( !presto_ispresto(dentry->d_inode) ) {
96                EXIT;
97                goto kml_out;
98        }
99
100        error = -EINVAL;
101        if ( ! presto_dentry2fset(dentry)) {
102                EXIT;
103                goto kml_out;
104        }
105
106        fset = presto_dentry2fset(dentry);
107        if (!fset) {
108                EXIT;
109                goto kml_out;
110        }
111        error = 0;
112        *recno = fset->fset_kml.fd_recno;
113
114 kml_out:
115        path_release(&nd);
116        return error;
117}
118
119static char * _izo_make_path(char *fsetname, char *name)
120{
121        char *path = NULL;
122        int len;
123
124        len = strlen("/.intermezzo/") + strlen(fsetname)
125                + 1 + strlen(name) + 1;
126
127        PRESTO_ALLOC(path, len);
128        if (path == NULL)
129                return NULL;
130
131        sprintf(path, "/.intermezzo/%s/%s", fsetname, name);
132
133        return path;
134}
135
136char * izo_make_path(struct presto_file_set *fset, char *name)
137{
138        return _izo_make_path(fset->fset_name, name);
139}
140
141static struct file *_izo_fset_open(char *fsetname, char *name, int flags, int mode)
142{
143        char *path;
144        struct file *f;
145        int error;
146        ENTRY;
147
148        path = _izo_make_path(fsetname, name);
149        if (path == NULL) {
150                EXIT;
151                return ERR_PTR(-ENOMEM);
152        }
153
154        CDEBUG(D_INODE, "opening file %s\n", path);
155        f = filp_open(path, flags, mode);
156        error = PTR_ERR(f);
157        if (IS_ERR(f)) {
158                CDEBUG(D_INODE, "Error %d\n", error);
159        }
160
161        PRESTO_FREE(path, strlen(path));
162
163        EXIT;
164        return f;
165
166}
167
168struct file *izo_fset_open(struct presto_file_set *fset, char *name, int flags, int mode)
169{
170        return _izo_fset_open(fset->fset_name, name, flags, mode);
171}
172
173
174
175/*
176 *  note: this routine "pins" a dentry for a fileset root
177 */
178int presto_set_fsetroot(struct dentry *ioctl_dentry, char *fsetname,
179                        unsigned int flags)
180{
181        struct presto_file_set *fset = NULL;
182        struct presto_cache *cache;
183        int error;
184        struct file  *fset_root;
185        struct dentry *dentry;
186
187        ENTRY;
188
189        fset_root = _izo_fset_open(fsetname, "ROOT",  O_RDONLY, 000);
190        if (IS_ERR(fset_root)) {
191                CERROR("Can't open %s/ROOT\n", fsetname);
192                EXIT;
193                error = PTR_ERR(fset_root);
194                goto out;
195        }
196        dentry = dget(fset_root->f_dentry);
197        filp_close(fset_root, NULL);
198
199        dentry->d_inode->i_op = ioctl_dentry->d_inode->i_op;
200        dentry->d_inode->i_fop = ioctl_dentry->d_inode->i_fop;
201        dentry->d_op = ioctl_dentry->d_op;
202        fset = presto_dentry2fset(dentry);
203        if (fset && (fset->fset_dentry == dentry) ) {
204                CERROR("Fsetroot already set (inode %ld)\n",
205                       dentry->d_inode->i_ino);
206        }
207
208        cache = presto_get_cache(dentry->d_inode);
209        if (!cache) {
210                CERROR("No cache found for inode %ld\n",
211                       dentry->d_inode->i_ino);
212                EXIT;
213                error = -ENODEV;
214                goto out_free;
215        }
216
217        PRESTO_ALLOC(fset, sizeof(*fset));
218        if ( !fset ) {
219                CERROR("No memory allocating fset for %s\n", fsetname);
220                EXIT;
221                error = -ENOMEM;
222                goto out_free;
223        }
224        CDEBUG(D_INODE, "fset at %p\n", fset);
225
226        CDEBUG(D_INODE, "InterMezzo: fsetroot: inode %ld, fileset name %s\n",
227               dentry->d_inode->i_ino, fsetname);
228
229        fset->fset_mnt = mntget(current->fs->pwdmnt);
230        fset->fset_cache = cache;
231        fset->fset_dentry = dentry;
232        fset->fset_name = strdup(fsetname);
233        fset->fset_chunkbits = CHUNK_BITS;
234        fset->fset_flags = flags;
235        fset->fset_file_maxio = FSET_DEFAULT_MAX_FILEIO;
236        fset->fset_permit_lock = SPIN_LOCK_UNLOCKED;
237        PRESTO_ALLOC(fset->fset_reint_buf, 64 * 1024);
238        if (fset->fset_reint_buf == NULL) {
239                EXIT;
240                error = -ENOMEM;
241                goto out_free;
242        }
243        init_waitqueue_head(&fset->fset_permit_queue);
244
245        if (presto_d2d(dentry) == NULL) {
246                dentry->d_fsdata = izo_alloc_ddata();
247        }
248        if (presto_d2d(dentry) == NULL) {
249                CERROR("InterMezzo: %s: no memory\n", __FUNCTION__);
250                EXIT;
251                error = -ENOMEM;
252                goto out_free;
253        }
254        presto_d2d(dentry)->dd_fset = fset;
255        list_add(&fset->fset_list, &cache->cache_fset_list);
256
257        error = izo_init_kml_file(fset, &fset->fset_kml);
258        if ( error ) {
259                EXIT;
260                CDEBUG(D_JOURNAL, "Error init_kml %d\n", error);
261                goto out_list_del;
262        }
263
264        error = izo_init_lml_file(fset, &fset->fset_lml);
265        if ( error ) {
266                int rc;
267                EXIT;
268                rc = izo_log_close(&fset->fset_kml);
269                CDEBUG(D_JOURNAL, "Error init_lml %d, cleanup %d\n", error, rc);
270                goto out_list_del;
271        }
272
273        /* init_last_rcvd_file could trigger a presto_file_write(), which
274         * requires that the lml structure be initialized. -phil */
275        error = izo_init_last_rcvd_file(fset, &fset->fset_rcvd);
276        if ( error ) {
277                int rc;
278                EXIT;
279                rc = izo_log_close(&fset->fset_kml);
280                rc = izo_log_close(&fset->fset_lml);
281                CDEBUG(D_JOURNAL, "Error init_lastrcvd %d, cleanup %d\n", error, rc);
282                goto out_list_del;
283        }
284
285        CDEBUG(D_PIOCTL, "-------> fset at %p, dentry at %p, mtpt %p,"
286               "fset %s, cache %p, presto_d2d(dentry)->dd_fset %p\n",
287               fset, dentry, fset->fset_dentry, fset->fset_name, cache,
288               presto_d2d(dentry)->dd_fset);
289
290        EXIT;
291        return 0;
292
293 out_list_del:
294        list_del(&fset->fset_list);
295        presto_d2d(dentry)->dd_fset = NULL;
296 out_free:
297        if (fset) {
298                mntput(fset->fset_mnt);
299                if (fset->fset_reint_buf != NULL)
300                        PRESTO_FREE(fset->fset_reint_buf, 64 * 1024);
301                PRESTO_FREE(fset, sizeof(*fset));
302        }
303        dput(dentry);
304 out:
305        return error;
306}
307
308static int izo_cleanup_fset(struct presto_file_set *fset)
309{
310        int error;
311        struct presto_cache *cache;
312
313        ENTRY;
314
315        CERROR("Cleaning up fset %s\n", fset->fset_name);
316
317        error = izo_log_close(&fset->fset_kml);
318        if (error)
319                CERROR("InterMezzo: Closing kml for fset %s: %d\n",
320                       fset->fset_name, error);
321        error = izo_log_close(&fset->fset_lml);
322        if (error)
323                CERROR("InterMezzo: Closing lml for fset %s: %d\n",
324                       fset->fset_name, error);
325        error = izo_log_close(&fset->fset_rcvd);
326        if (error)
327                CERROR("InterMezzo: Closing last_rcvd for fset %s: %d\n",
328                       fset->fset_name, error);
329
330        cache = fset->fset_cache;
331
332        list_del(&fset->fset_list);
333
334        presto_d2d(fset->fset_dentry)->dd_fset = NULL;
335        dput(fset->fset_dentry);
336        mntput(fset->fset_mnt);
337
338        PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) + 1);
339        PRESTO_FREE(fset->fset_reint_buf, 64 * 1024);
340        PRESTO_FREE(fset, sizeof(*fset));
341        EXIT;
342        return error;
343}
344
345int izo_clear_fsetroot(struct dentry *dentry)
346{
347        struct presto_file_set *fset;
348
349        ENTRY;
350
351        fset = presto_dentry2fset(dentry);
352        if (!fset) {
353                EXIT;
354                return -EINVAL;
355        }
356
357        izo_cleanup_fset(fset);
358        EXIT;
359        return 0;
360}
361
362int izo_clear_all_fsetroots(struct presto_cache *cache)
363{
364        struct presto_file_set *fset;
365        struct list_head *tmp,*tmpnext;
366        int error;
367
368        error = 0;
369        tmp = &cache->cache_fset_list;
370        tmpnext = tmp->next;
371        while ( tmpnext != &cache->cache_fset_list) {
372                tmp = tmpnext;
373                tmpnext = tmp->next;
374                fset = list_entry(tmp, struct presto_file_set, fset_list);
375
376                error = izo_cleanup_fset(fset);
377                if (error)
378                        break;
379        }
380        return error;
381}
382
383static struct vfsmount *izo_alloc_vfsmnt(void)
384{
385        struct vfsmount *mnt;
386        PRESTO_ALLOC(mnt, sizeof(*mnt));
387        if (mnt) {
388                memset(mnt, 0, sizeof(struct vfsmount));
389                atomic_set(&mnt->mnt_count,1);
390                INIT_LIST_HEAD(&mnt->mnt_hash);
391                INIT_LIST_HEAD(&mnt->mnt_child);
392                INIT_LIST_HEAD(&mnt->mnt_mounts);
393                INIT_LIST_HEAD(&mnt->mnt_list);
394        }
395        return mnt;
396}
397
398
399static void izo_setup_ctxt(struct dentry *root, struct vfsmount *mnt,
400                           struct run_ctxt *save)
401{
402        struct run_ctxt new;
403
404        mnt->mnt_root = root;
405        mnt->mnt_sb = root->d_inode->i_sb;
406        unlock_super(mnt->mnt_sb);
407
408        new.rootmnt = mnt;
409        new.root = root;
410        new.pwdmnt = mnt;
411        new.pwd = root;
412        new.fsuid = 0;
413        new.fsgid = 0;
414        new.fs = get_fs();
415        new.ngroups = 0;
416
417        push_ctxt(save, &new);
418}
419
420static void izo_cleanup_ctxt(struct vfsmount *mnt, struct run_ctxt *save)
421{
422        lock_super(mnt->mnt_sb);
423        pop_ctxt(save);
424}
425
426static int izo_simple_mkdir(struct dentry *dir, char *name, int mode)
427{
428        struct dentry *dchild;
429        int err;
430        ENTRY;
431
432        dchild = lookup_one_len(name, dir, strlen(name));
433        if (IS_ERR(dchild)) {
434                EXIT;
435                return PTR_ERR(dchild);
436        }
437
438        if (dchild->d_inode) {
439                dput(dchild);
440                EXIT;
441                return -EEXIST;
442        }
443
444        err = vfs_mkdir(dir->d_inode, dchild, mode);
445        dput(dchild);
446
447        EXIT;
448        return err;
449}
450
451static int izo_simple_symlink(struct dentry *dir, char *name, char *tgt)
452{
453        struct dentry *dchild;
454        int err;
455        ENTRY;
456
457        dchild = lookup_one_len(name, dir, strlen(name));
458        if (IS_ERR(dchild)) {
459                EXIT;
460                return PTR_ERR(dchild);
461        }
462
463        if (dchild->d_inode) {
464                dput(dchild);
465                EXIT;
466                return -EEXIST;
467        }
468
469        err = vfs_symlink(dir->d_inode, dchild, tgt);
470        dput(dchild);
471
472        EXIT;
473        return err;
474}
475
476/*
477 * run set_fsetroot in chroot environment
478 */
479int presto_set_fsetroot_from_ioc(struct dentry *root, char *fsetname,
480                                 unsigned int flags)
481{
482        int rc;
483        struct presto_cache *cache;
484        struct vfsmount *mnt;
485        struct run_ctxt save;
486
487        if (root != root->d_inode->i_sb->s_root) {
488                CERROR ("IOC_SET_FSET must be called on mount point\n");
489                return -ENODEV;
490        }
491
492        cache = presto_get_cache(root->d_inode);
493        mnt = cache->cache_vfsmount;
494        if (!mnt) {
495                EXIT;
496                return -ENOMEM;
497        }
498
499        izo_setup_ctxt(root, mnt, &save);
500        rc = presto_set_fsetroot(root, fsetname, flags);
501        izo_cleanup_ctxt(mnt, &save);
502        return rc;
503}
504
505int izo_prepare_fileset(struct dentry *root, char *fsetname)
506{
507        int err;
508        struct dentry *dotizo = NULL, *fsetdir = NULL, *dotiopen = NULL;
509        struct presto_cache *cache;
510        struct vfsmount *mnt;
511        struct run_ctxt save;
512
513        cache = presto_get_cache(root->d_inode);
514        mnt = cache->cache_vfsmount = izo_alloc_vfsmnt();
515        if (!mnt) {
516                EXIT;
517                return -ENOMEM;
518        }
519
520        if (!fsetname)
521                fsetname = "rootfset";
522
523        izo_setup_ctxt(root, mnt, &save);
524
525        err = izo_simple_mkdir(root, ".intermezzo", 0755);
526        CDEBUG(D_CACHE, "mkdir on .intermezzo err %d\n", err);
527
528        err = izo_simple_mkdir(root, "..iopen..", 0755);
529        CDEBUG(D_CACHE, "mkdir on ..iopen.. err %d\n", err);
530
531        dotiopen = lookup_one_len("..iopen..", root, strlen("..iopen.."));
532        if (IS_ERR(dotiopen)) {
533                EXIT;
534                goto out;
535        }
536        dotiopen->d_inode->i_op = &presto_dir_iops;
537        dput(dotiopen);
538
539
540        dotizo = lookup_one_len(".intermezzo", root, strlen(".intermezzo"));
541        if (IS_ERR(dotizo)) {
542                EXIT;
543                goto out;
544        }
545
546
547        err = izo_simple_mkdir(dotizo, fsetname, 0755);
548        CDEBUG(D_CACHE, "mkdir err %d\n", err);
549
550        fsetdir = lookup_one_len(fsetname, dotizo, strlen(fsetname));
551        if (IS_ERR(fsetdir)) {
552                EXIT;
553                goto out;
554        }
555
556        err = izo_simple_symlink(fsetdir, "ROOT", "../..");
557
558        err =  presto_set_fsetroot(root, fsetname, 0);
559        CDEBUG(D_CACHE, "set_fsetroot err %d\n", err);
560
561 out:
562        if (dotizo && !IS_ERR(dotizo))
563                dput(dotizo);
564        if (fsetdir && !IS_ERR(fsetdir))
565                dput(fsetdir);
566        izo_cleanup_ctxt(mnt, &save);
567        return err;
568}
569
570int izo_set_fileid(struct file *dir, struct izo_ioctl_data *data)
571{
572        int rc = 0;
573        struct presto_cache *cache;
574        struct vfsmount *mnt;
575        struct run_ctxt save;
576        struct nameidata nd;
577        struct dentry *dentry;
578        struct presto_dentry_data *dd;
579        struct dentry *root;
580        char *buf = NULL;
581
582        ENTRY;
583
584
585        root = dir->f_dentry;
586
587        /* actually, needs to be called on ROOT of fset, not mount point
588        if (root != root->d_inode->i_sb->s_root) {
589                CERROR ("IOC_SET_FSET must be called on mount point\n");
590                return -ENODEV;
591        }
592        */
593
594        cache = presto_get_cache(root->d_inode);
595        mnt = cache->cache_vfsmount;
596        if (!mnt) {
597                EXIT;
598                return -ENOMEM;
599        }
600
601        izo_setup_ctxt(root, mnt, &save);
602
603        PRESTO_ALLOC(buf, data->ioc_plen1);
604        if (!buf) {
605                rc = -ENOMEM;
606                EXIT;
607                goto out;
608        }
609        if (copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) {
610                rc =  -EFAULT;
611                EXIT;
612                goto out;
613        }
614
615        rc = presto_walk(buf, &nd);
616        if (rc) {
617                CERROR("Unable to open: %s\n", buf);
618                EXIT;
619                goto out;
620        }
621        dentry = nd.dentry;
622        if (!dentry) {
623                CERROR("no dentry!\n");
624                rc =  -EINVAL;
625                EXIT;
626                goto out_close;
627        }
628        dd = presto_d2d(dentry);
629        if (!dd) {
630                CERROR("no dentry_data!\n");
631                rc = -EINVAL;
632                EXIT;
633                goto out_close;
634        }
635
636        CDEBUG(D_FILE,"de:%p dd:%p\n", dentry, dd);
637
638        if (dd->remote_ino != 0) {
639                CERROR("remote_ino already set? %Lx:%Lx\n", dd->remote_ino,
640                       dd->remote_generation);
641                rc = 0;
642                EXIT;
643                goto out_close;
644        }
645
646
647        CDEBUG(D_FILE,"setting %p %p, %s to %Lx:%Lx\n", dentry, dd,
648               buf, data->ioc_ino,
649               data->ioc_generation);
650        dd->remote_ino = data->ioc_ino;
651        dd->remote_generation = data->ioc_generation;
652
653        EXIT;
654 out_close:
655        path_release(&nd);
656 out:
657        if (buf)
658                PRESTO_FREE(buf, data->ioc_plen1);
659        izo_cleanup_ctxt(mnt, &save);
660        return rc;
661}
662