/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- * vim:expandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2000 Stelias Computing, Inc. * Copyright (C) 2000 Red Hat, Inc. * Copyright (C) 2000 Tacitus Systems * Copyright (C) 2000 Peter J. Braam * * This file is part of InterMezzo, http://www.inter-mezzo.org. * * InterMezzo is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * InterMezzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with InterMezzo; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "intermezzo_fs.h" #include "intermezzo_psdev.h" static inline void presto_relock_sem(struct inode *dir) { /* the lock from sys_mkdir / lookup_create */ down(&dir->i_sem); /* the rest is done by the do_{create,mkdir, ...} */ } static inline void presto_relock_other(struct inode *dir) { /* vfs_mkdir locks */ // down(&dir->i_zombie); //lock_kernel(); } static inline void presto_fulllock(struct inode *dir) { /* the lock from sys_mkdir / lookup_create */ down(&dir->i_sem); /* vfs_mkdir locks */ // down(&dir->i_zombie); //lock_kernel(); } static inline void presto_unlock(struct inode *dir) { /* vfs_mkdir locks */ //unlock_kernel(); // up(&dir->i_zombie); /* the lock from sys_mkdir / lookup_create */ up(&dir->i_sem); } /* * these are initialized in super.c */ extern int presto_permission(struct inode *inode, int mask, struct nameidata *nd); static int izo_authorized_uid; int izo_dentry_is_ilookup(struct dentry *dentry, ino_t *id, unsigned int *generation) { char tmpname[64]; char *next; ENTRY; /* prefix is 7 characters: '...ino:' */ if ( dentry->d_name.len < 7 || dentry->d_name.len > 64 || memcmp(dentry->d_name.name, PRESTO_ILOOKUP_MAGIC, 7) != 0 ) { EXIT; return 0; } memcpy(tmpname, dentry->d_name.name + 7, dentry->d_name.len - 7); *(tmpname + dentry->d_name.len - 7) = '\0'; /* name is of the form ...ino:: */ *id = simple_strtoul(tmpname, &next, 16); if ( *next == PRESTO_ILOOKUP_SEP ) { *generation = simple_strtoul(next + 1, 0, 16); CDEBUG(D_INODE, "ino string: %s, Id = %lx (%lu), " "generation %x (%d)\n", tmpname, *id, *id, *generation, *generation); EXIT; return 1; } else { EXIT; return 0; } } struct dentry *presto_tmpfs_ilookup(struct inode *dir, struct dentry *dentry, ino_t ino, unsigned int generation) { return dentry; } inline int presto_can_ilookup(void) { return (current->euid == izo_authorized_uid || capable(CAP_DAC_READ_SEARCH)); } struct dentry *presto_iget_ilookup(struct inode *dir, struct dentry *dentry, ino_t ino, unsigned int generation) { struct inode *inode; int error; ENTRY; if ( !presto_can_ilookup() ) { CERROR("ilookup denied: euid %u, authorized_uid %u\n", current->euid, izo_authorized_uid); return ERR_PTR(-EPERM); } error = -ENOENT; inode = iget(dir->i_sb, ino); if (!inode) { CERROR("fatal: NULL inode ino %lu\n", ino); goto cleanup_iput; } if (is_bad_inode(inode) || inode->i_nlink == 0) { CERROR("fatal: bad inode ino %lu, links %d\n", ino, inode->i_nlink); goto cleanup_iput; } if (inode->i_generation != generation) { CERROR("fatal: bad generation %u (want %u)\n", inode->i_generation, generation); goto cleanup_iput; } d_instantiate(dentry, inode); dentry->d_flags |= DCACHE_DISCONNECTED; /* NFS hack */ EXIT; return NULL; cleanup_iput: if (inode) iput(inode); return ERR_PTR(error); } struct dentry *presto_add_ilookup_dentry(struct dentry *parent, struct dentry *real) { struct inode *inode = real->d_inode; struct dentry *de; char buf[32]; char *ptr = buf; struct dentry *inodir; struct presto_dentry_data *dd; inodir = lookup_one_len("..iopen..", parent, strlen("..iopen..")); if (!inodir || IS_ERR(inodir) || !inodir->d_inode ) { CERROR("%s: bad ..iopen.. lookup\n", __FUNCTION__); return NULL; } inodir->d_inode->i_op = &presto_dir_iops; snprintf(ptr, 32, "...ino:%lx:%x", inode->i_ino, inode->i_generation); de = lookup_one_len(ptr, inodir, strlen(ptr)); if (!de || IS_ERR(de)) { CERROR("%s: bad ...ino lookup %ld\n", __FUNCTION__, PTR_ERR(de)); dput(inodir); return NULL; } dd = presto_d2d(real); if (!dd) BUG(); /* already exists */ if (de->d_inode) BUG(); #if 0 if (de->d_inode != inode ) { CERROR("XX de->d_inode %ld, inode %ld\n", de->d_inode->i_ino, inode->i_ino); BUG(); } if (dd->dd_inodentry) { CERROR("inodentry exists %ld \n", inode->i_ino); BUG(); } dput(inodir); return de; } #endif if (presto_d2d(de)) BUG(); atomic_inc(&inode->i_count); de->d_op = &presto_dentry_ops; d_add(de, inode); if (!de->d_op) CERROR("DD: no ops dentry %p, dd %p\n", de, dd); dd->dd_inodentry = de; dd->dd_count++; de->d_fsdata = dd; dput(inodir); return de; } struct dentry *presto_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) { int rc = 0; struct dentry *de; struct presto_cache *cache; int minor; ino_t ino; unsigned int generation; struct inode_operations *iops; int is_ilookup = 0; ENTRY; cache = presto_get_cache(dir); if (cache == NULL) { CERROR("InterMezzo BUG: no cache in presto_lookup " "(dir ino: %ld)!\n", dir->i_ino); EXIT; return NULL; } minor = presto_c2m(cache); iops = filter_c2cdiops(cache->cache_filter); if (!iops || !iops->lookup) { CERROR("InterMezzo BUG: filesystem has no lookup\n"); EXIT; return NULL; } CDEBUG(D_CACHE, "dentry %p, dir ino: %ld, name: %*s, islento: %d\n", dentry, dir->i_ino, dentry->d_name.len, dentry->d_name.name, ISLENTO(minor)); if (dentry->d_fsdata) CERROR("DD -- BAD dentry %p has data\n", dentry); dentry->d_fsdata = NULL; #if 0 if (ext2_check_for_iopen(dir, dentry)) de = NULL; else { #endif if ( izo_dentry_is_ilookup(dentry, &ino, &generation) ) { de = cache->cache_filter->o_trops->tr_ilookup (dir, dentry, ino, generation); is_ilookup = 1; } else de = iops->lookup(dir, dentry, nd); #if 0 } #endif if ( IS_ERR(de) ) { CERROR("dentry lookup error %ld\n", PTR_ERR(de)); return de; } /* some file systems have no read_inode: set methods here */ if (dentry->d_inode) presto_set_ops(dentry->d_inode, cache->cache_filter); filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, &presto_dentry_ops); dentry->d_op = filter_c2udops(cache->cache_filter); /* In lookup we will tolerate EROFS return codes from presto_set_dd * to placate NFS. EROFS indicates that a fileset was not found but * we should still be able to continue through a lookup. * Anything else is a hard error and must be returned to VFS. */ if (!is_ilookup) rc = presto_set_dd(dentry); if (rc && rc != -EROFS) { CERROR("presto_set_dd failed (dir %ld, name %*s): %d\n", dir->i_ino, dentry->d_name.len, dentry->d_name.name, rc); return ERR_PTR(rc); } EXIT; return NULL; } static inline int presto_check_set_fsdata (struct dentry *de) { if (presto_d2d(de) == NULL) { #ifdef PRESTO_NO_NFS CERROR("dentry without fsdata: %p: %*s\n", de, de->d_name.len, de->d_name.name); BUG(); #endif return presto_set_dd (de); } return 0; } int presto_setattr(struct dentry *de, struct iattr *iattr) { int error; struct presto_cache *cache; struct presto_file_set *fset; struct lento_vfs_context info = { 0, {0}, 0 }; ENTRY; error = presto_prep(de, &cache, &fset); if ( error ) { EXIT; return error; } if (!iattr->ia_valid) CDEBUG(D_INODE, "presto_setattr: iattr is not valid\n"); CDEBUG(D_INODE, "valid %#x, mode %#o, uid %u, gid %u, size %Lu, " "atime %lu mtime %lu ctime %lu flags %d\n", iattr->ia_valid, iattr->ia_mode, iattr->ia_uid, iattr->ia_gid, iattr->ia_size, iattr->ia_atime.tv_sec, iattr->ia_mtime.tv_sec, iattr->ia_ctime.tv_sec, iattr->ia_attr_flags); if ( presto_get_permit(de->d_inode) < 0 ) { EXIT; return -EROFS; } if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_setattr(fset, de, iattr, &info); presto_put_permit(de->d_inode); return error; } /* * Now the meat: the fs operations that require journaling * * * XXX: some of these need modifications for hierarchical filesets */ int presto_prep(struct dentry *dentry, struct presto_cache **cache, struct presto_file_set **fset) { int rc; /* NFS might pass us dentries which have not gone through lookup. * Test and set d_fsdata for such dentries */ rc = presto_check_set_fsdata (dentry); if (rc) return rc; *fset = presto_fset(dentry); if ( *fset == NULL ) { CERROR("No file set for dentry at %p: %*s\n", dentry, dentry->d_name.len, dentry->d_name.name); return -EROFS; } *cache = (*fset)->fset_cache; if ( *cache == NULL ) { CERROR("PRESTO: BAD, BAD: cannot find cache\n"); return -EBADF; } CDEBUG(D_PIOCTL, "---> cache flags %x, fset flags %x\n", (*cache)->cache_flags, (*fset)->fset_flags); if( presto_is_read_only(*fset) ) { CERROR("PRESTO: cannot modify read-only fileset, minor %d.\n", presto_c2m(*cache)); return -EROFS; } return 0; } static int presto_create(struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd) { int error; struct presto_cache *cache; struct dentry *parent = dentry->d_parent; struct lento_vfs_context info; struct presto_file_set *fset; ENTRY; error = presto_check_set_fsdata(dentry); if ( error ) { EXIT; return error; } error = presto_prep(dentry->d_parent, &cache, &fset); if ( error ) { EXIT; return error; } presto_unlock(dir); /* Does blocking and non-blocking behavious need to be checked for. Without blocking (return 1), the permit was acquired without reintegration */ if ( presto_get_permit(dir) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } presto_relock_sem(dir); parent = dentry->d_parent; memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_create(fset, parent, dentry, mode, &info); presto_relock_other(dir); presto_put_permit(dir); EXIT; return error; } static int presto_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { int error; struct presto_cache *cache, *new_cache; struct presto_file_set *fset, *new_fset; struct dentry *parent = new_dentry->d_parent; struct lento_vfs_context info; ENTRY; error = presto_prep(old_dentry, &cache, &fset); if ( error ) { EXIT; return error; } error = presto_check_set_fsdata(new_dentry); if ( error ) { EXIT; return error; } error = presto_prep(new_dentry->d_parent, &new_cache, &new_fset); if ( error ) { EXIT; return error; } if (fset != new_fset) { EXIT; return -EXDEV; } presto_unlock(dir); if ( presto_get_permit(old_dentry->d_inode) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } if ( presto_get_permit(dir) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } presto_relock_sem(dir); parent = new_dentry->d_parent; memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_link(fset, old_dentry, parent, new_dentry, &info); #if 0 /* XXX for links this is not right */ if (cache->cache_filter->o_trops->tr_add_ilookup ) { struct dentry *d; d = cache->cache_filter->o_trops->tr_add_ilookup (dir->i_sb->s_root, new_dentry, 1); } #endif presto_relock_other(dir); presto_put_permit(dir); presto_put_permit(old_dentry->d_inode); return error; } static int presto_mkdir(struct inode * dir, struct dentry * dentry, int mode) { int error; struct presto_file_set *fset; struct presto_cache *cache; struct dentry *parent = dentry->d_parent; struct lento_vfs_context info; ENTRY; error = presto_check_set_fsdata(dentry); if ( error ) { EXIT; return error; } error = presto_prep(dentry->d_parent, &cache, &fset); if ( error ) { EXIT; return error; } presto_unlock(dir); if ( presto_get_permit(dir) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; presto_relock_sem(dir); parent = dentry->d_parent; error = presto_do_mkdir(fset, parent, dentry, mode, &info); presto_relock_other(dir); presto_put_permit(dir); return error; } static int presto_symlink(struct inode *dir, struct dentry *dentry, const char *name) { int error; struct presto_cache *cache; struct presto_file_set *fset; struct dentry *parent = dentry->d_parent; struct lento_vfs_context info; ENTRY; error = presto_check_set_fsdata(dentry); if ( error ) { EXIT; return error; } error = presto_prep(dentry->d_parent, &cache, &fset); if ( error ) { EXIT; return error; } presto_unlock(dir); if ( presto_get_permit(dir) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } presto_relock_sem(dir); parent = dentry->d_parent; memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_symlink(fset, parent, dentry, name, &info); presto_relock_other(dir); presto_put_permit(dir); return error; } int presto_unlink(struct inode *dir, struct dentry *dentry) { int error; struct presto_cache *cache; struct presto_file_set *fset; struct dentry *parent = dentry->d_parent; struct lento_vfs_context info; ENTRY; error = presto_check_set_fsdata(dentry); if ( error ) { EXIT; return error; } error = presto_prep(dentry->d_parent, &cache, &fset); if ( error ) { EXIT; return error; } presto_unlock(dir); if ( presto_get_permit(dir) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } presto_relock_sem(dir); parent = dentry->d_parent; memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_unlink(fset, parent, dentry, &info); presto_relock_other(dir); presto_put_permit(dir); return error; } static int presto_rmdir(struct inode *dir, struct dentry *dentry) { int error; struct presto_cache *cache; struct presto_file_set *fset; struct dentry *parent = dentry->d_parent; struct lento_vfs_context info; ENTRY; CDEBUG(D_FILE, "prepping presto\n"); error = presto_check_set_fsdata(dentry); if ( error ) { EXIT; return error; } error = presto_prep(dentry->d_parent, &cache, &fset); if ( error ) { EXIT; return error; } CDEBUG(D_FILE, "unlocking\n"); /* We need to dget() before the dput in double_unlock, to ensure we * still have dentry references. double_lock doesn't do dget for us. */ if (d_unhashed(dentry)) d_rehash(dentry); // double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); up(&dentry->d_inode->i_sem); up(&dir->i_sem); CDEBUG(D_FILE, "getting permit\n"); if ( presto_get_permit(parent->d_inode) < 0 ) { EXIT; down(&dir->i_sem); down(&dentry->d_inode->i_sem); // double_down(&dir->i_sem, &dentry->d_inode->i_sem); // double_down(&dir->i_zombie, &dentry->d_inode->i_zombie); lock_kernel(); return -EROFS; } CDEBUG(D_FILE, "locking\n"); down(&dir->i_sem); down(&dentry->d_inode->i_sem); parent = dentry->d_parent; memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_rmdir(fset, parent, dentry, &info); presto_put_permit(parent->d_inode); lock_kernel(); EXIT; return error; } static int presto_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) { int error; struct presto_cache *cache; struct presto_file_set *fset; struct dentry *parent = dentry->d_parent; struct lento_vfs_context info; if (!old_valid_dev(rdev)) return -EINVAL; ENTRY; error = presto_check_set_fsdata(dentry); if ( error ) { EXIT; return error; } error = presto_prep(dentry->d_parent, &cache, &fset); if ( error ) { EXIT; return error; } presto_unlock(dir); if ( presto_get_permit(dir) < 0 ) { EXIT; presto_fulllock(dir); return -EROFS; } presto_relock_sem(dir); parent = dentry->d_parent; memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = presto_do_mknod(fset, parent, dentry, mode, rdev, &info); presto_relock_other(dir); presto_put_permit(dir); EXIT; return error; } // XXX this can be optimized: renamtes across filesets only require // multiple KML records, but can locally be executed normally. int presto_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int error; struct presto_cache *cache, *new_cache; struct presto_file_set *fset, *new_fset; struct lento_vfs_context info; struct dentry *old_parent = old_dentry->d_parent; struct dentry *new_parent = new_dentry->d_parent; int triple; ENTRY; error = presto_prep(old_dentry, &cache, &fset); if ( error ) { EXIT; return error; } error = presto_prep(new_parent, &new_cache, &new_fset); if ( error ) { EXIT; return error; } if ( fset != new_fset ) { EXIT; return -EXDEV; } /* We need to do dget before the dput in double_unlock, to ensure we * still have dentry references. double_lock doesn't do dget for us. */ triple = (S_ISDIR(old_dentry->d_inode->i_mode) && new_dentry->d_inode)? 1:0; unlock_rename(new_dentry->d_parent, old_dentry->d_parent); if ( presto_get_permit(old_dir) < 0 ) { EXIT; return -EROFS; } if ( presto_get_permit(new_dir) < 0 ) { EXIT; return -EROFS; } lock_rename(new_dentry->d_parent, old_dentry->d_parent); memset(&info, 0, sizeof(info)); if (!ISLENTO(presto_c2m(cache))) info.flags = LENTO_FL_KML; info.flags |= LENTO_FL_IGNORE_TIME; error = do_rename(fset, old_parent, old_dentry, new_parent, new_dentry, &info); presto_put_permit(new_dir); presto_put_permit(old_dir); return error; } /* basically this allows the ilookup processes access to all files for * reading, while not making ilookup totally insecure. This could all * go away if we could set the CAP_DAC_READ_SEARCH capability for the client. */ /* If posix acls are available, the underlying cache fs will export the * appropriate permission function. Thus we do not worry here about ACLs * or EAs. -SHP */ int presto_permission(struct inode *inode, int mask, struct nameidata *nd) { unsigned short mode = inode->i_mode; struct presto_cache *cache; int rc; ENTRY; if ( presto_can_ilookup() && !(mask & S_IWOTH)) { CDEBUG(D_CACHE, "ilookup on %ld OK\n", inode->i_ino); EXIT; return 0; } cache = presto_get_cache(inode); if ( cache ) { /* we only override the file/dir permission operations */ struct inode_operations *fiops = filter_c2cfiops(cache->cache_filter); struct inode_operations *diops = filter_c2cdiops(cache->cache_filter); if ( S_ISREG(mode) && fiops && fiops->permission ) { EXIT; return fiops->permission(inode, mask, nd); } if ( S_ISDIR(mode) && diops && diops->permission ) { EXIT; return diops->permission(inode, mask, nd); } } /* The cache filesystem doesn't have its own permission function, * so we call the default one. */ rc = vfs_permission(inode, mask); EXIT; return rc; } int presto_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { char buf[1024]; struct izo_ioctl_data *data = NULL; struct presto_dentry_data *dd; int rc; ENTRY; /* Try the filesystem's ioctl first, and return if it succeeded. */ dd = presto_d2d(file->f_dentry); if (dd && dd->dd_fset) { int (*cache_ioctl)(struct inode *, struct file *, unsigned int, unsigned long ) = filter_c2cdfops(dd->dd_fset->fset_cache->cache_filter)->ioctl; rc = -ENOTTY; if (cache_ioctl) rc = cache_ioctl(inode, file, cmd, arg); if (rc != -ENOTTY) { EXIT; return rc; } } if (current->euid != 0 && current->euid != izo_authorized_uid) { EXIT; return -EPERM; } memset(buf, 0, sizeof(buf)); if (izo_ioctl_getdata(buf, buf + 1024, (void *)arg)) { CERROR("intermezzo ioctl: data error\n"); return -EINVAL; } data = (struct izo_ioctl_data *)buf; switch(cmd) { case IZO_IOC_REINTKML: { int rc; int cperr; rc = kml_reint_rec(file, data); EXIT; cperr = copy_to_user((char *)arg, data, sizeof(*data)); if (cperr) { CERROR("WARNING: cperr %d\n", cperr); rc = -EFAULT; } return rc; } case IZO_IOC_GET_RCVD: { struct izo_rcvd_rec rec; struct presto_file_set *fset; int rc; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } rc = izo_rcvd_get(&rec, fset, data->ioc_uuid); if (rc < 0) { EXIT; return rc; } EXIT; return copy_to_user((char *)arg, &rec, sizeof(rec))? -EFAULT : 0; } case IZO_IOC_REPSTATUS: { __u64 client_kmlsize; struct izo_rcvd_rec *lr_client; struct izo_rcvd_rec rec; struct presto_file_set *fset; int minor; int rc; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); client_kmlsize = data->ioc_kmlsize; lr_client = (struct izo_rcvd_rec *) data->ioc_pbuf1; rc = izo_repstatus(fset, client_kmlsize, lr_client, &rec); if (rc < 0) { EXIT; return rc; } EXIT; return copy_to_user((char *)arg, &rec, sizeof(rec))? -EFAULT : 0; } case IZO_IOC_GET_CHANNEL: { struct presto_file_set *fset; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } data->ioc_dev = fset->fset_cache->cache_psdev->uc_minor; CDEBUG(D_PSDEV, "CHANNEL %d\n", data->ioc_dev); EXIT; return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0; } case IZO_IOC_SET_IOCTL_UID: izo_authorized_uid = data->ioc_uid; EXIT; return 0; case IZO_IOC_SET_PID: rc = izo_psdev_setpid(data->ioc_dev); EXIT; return rc; case IZO_IOC_SET_CHANNEL: rc = izo_psdev_setchannel(file, data->ioc_dev); EXIT; return rc; case IZO_IOC_GET_KML_SIZE: { struct presto_file_set *fset; __u64 kmlsize; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } kmlsize = presto_kml_offset(fset) + fset->fset_kml_logical_off; EXIT; return copy_to_user((char *)arg, &kmlsize, sizeof(kmlsize))?-EFAULT : 0; } case IZO_IOC_PURGE_FILE_DATA: { struct presto_file_set *fset; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } rc = izo_purge_file(fset, data->ioc_inlbuf1); EXIT; return rc; } case IZO_IOC_GET_FILEID: { rc = izo_get_fileid(file, data); EXIT; if (rc) return rc; return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0; } case IZO_IOC_SET_FILEID: { rc = izo_set_fileid(file, data); EXIT; if (rc) return rc; return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0; } case IZO_IOC_ADJUST_LML: { struct lento_vfs_context *info; info = (struct lento_vfs_context *)data->ioc_inlbuf1; rc = presto_adjust_lml(file, info); EXIT; return rc; } case IZO_IOC_CONNECT: { struct presto_file_set *fset; int minor; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); rc = izo_upc_connect(minor, data->ioc_ino, data->ioc_generation, data->ioc_uuid, data->ioc_flags); EXIT; return rc; } case IZO_IOC_GO_FETCH_KML: { struct presto_file_set *fset; int minor; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); rc = izo_upc_go_fetch_kml(minor, fset->fset_name, data->ioc_uuid, data->ioc_kmlsize); EXIT; return rc; } case IZO_IOC_REVOKE_PERMIT: if (data->ioc_flags) rc = izo_revoke_permit(file->f_dentry, data->ioc_uuid); else rc = izo_revoke_permit(file->f_dentry, NULL); EXIT; return rc; case IZO_IOC_CLEAR_FSET: rc = izo_clear_fsetroot(file->f_dentry); EXIT; return rc; case IZO_IOC_CLEAR_ALL_FSETS: { struct presto_file_set *fset; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } rc = izo_clear_all_fsetroots(fset->fset_cache); EXIT; return rc; } case IZO_IOC_SET_FSET: /* * Mark this dentry as being a fileset root. */ rc = presto_set_fsetroot_from_ioc(file->f_dentry, data->ioc_inlbuf1, data->ioc_flags); EXIT; return rc; case IZO_IOC_MARK: { int res = 0; /* resulting flags - returned to user */ int error; CDEBUG(D_DOWNCALL, "mark inode: %ld, and: %x, or: %x, what %d\n", file->f_dentry->d_inode->i_ino, data->ioc_and_flag, data->ioc_or_flag, data->ioc_mark_what); switch (data->ioc_mark_what) { case MARK_DENTRY: error = izo_mark_dentry(file->f_dentry, data->ioc_and_flag, data->ioc_or_flag, &res); break; case MARK_FSET: error = izo_mark_fset(file->f_dentry, data->ioc_and_flag, data->ioc_or_flag, &res); break; case MARK_CACHE: error = izo_mark_cache(file->f_dentry, data->ioc_and_flag, data->ioc_or_flag, &res); break; case MARK_GETFL: { int fflags, cflags; data->ioc_and_flag = 0xffffffff; data->ioc_or_flag = 0; error = izo_mark_dentry(file->f_dentry, data->ioc_and_flag, data->ioc_or_flag, &res); if (error) break; error = izo_mark_fset(file->f_dentry, data->ioc_and_flag, data->ioc_or_flag, &fflags); if (error) break; error = izo_mark_cache(file->f_dentry, data->ioc_and_flag, data->ioc_or_flag, &cflags); if (error) break; data->ioc_and_flag = fflags; data->ioc_or_flag = cflags; break; } default: error = -EINVAL; } if (error) { EXIT; return error; } data->ioc_mark_what = res; CDEBUG(D_DOWNCALL, "mark inode: %ld, and: %x, or: %x, what %x\n", file->f_dentry->d_inode->i_ino, data->ioc_and_flag, data->ioc_or_flag, data->ioc_mark_what); EXIT; return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0; } #if 0 case IZO_IOC_CLIENT_MAKE_BRANCH: { struct presto_file_set *fset; int minor; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); rc = izo_upc_client_make_branch(minor, fset->fset_name, data->ioc_inlbuf1, data->ioc_inlbuf2); EXIT; return rc; } #endif case IZO_IOC_SERVER_MAKE_BRANCH: { struct presto_file_set *fset; int minor; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); izo_upc_server_make_branch(minor, data->ioc_inlbuf1); EXIT; return 0; } case IZO_IOC_SET_KMLSIZE: { struct presto_file_set *fset; int minor; struct izo_rcvd_rec rec; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); rc = izo_upc_set_kmlsize(minor, fset->fset_name, data->ioc_uuid, data->ioc_kmlsize); if (rc != 0) { EXIT; return rc; } rc = izo_rcvd_get(&rec, fset, data->ioc_uuid); if (rc == -EINVAL) { /* We don't know anything about this uuid yet; no * worries. */ memset(&rec, 0, sizeof(rec)); } else if (rc <= 0) { CERROR("InterMezzo: error reading last_rcvd: %d\n", rc); EXIT; return rc; } rec.lr_remote_offset = data->ioc_kmlsize; rc = izo_rcvd_write(fset, &rec); if (rc <= 0) { CERROR("InterMezzo: error writing last_rcvd: %d\n", rc); EXIT; return rc; } EXIT; return rc; } case IZO_IOC_BRANCH_UNDO: { struct presto_file_set *fset; int minor; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); rc = izo_upc_branch_undo(minor, fset->fset_name, data->ioc_inlbuf1); EXIT; return rc; } case IZO_IOC_BRANCH_REDO: { struct presto_file_set *fset; int minor; fset = presto_fset(file->f_dentry); if (fset == NULL) { EXIT; return -ENODEV; } minor = presto_f2m(fset); rc = izo_upc_branch_redo(minor, fset->fset_name, data->ioc_inlbuf1); EXIT; return rc; } default: EXIT; return -ENOTTY; } EXIT; return 0; } struct file_operations presto_dir_fops = { .ioctl = presto_ioctl }; struct inode_operations presto_dir_iops = { .create = presto_create, .lookup = presto_lookup, .link = presto_link, .unlink = presto_unlink, .symlink = presto_symlink, .mkdir = presto_mkdir, .rmdir = presto_rmdir, .mknod = presto_mknod, .rename = presto_rename, .permission = presto_permission, .setattr = presto_setattr, #ifdef CONFIG_FS_EXT_ATTR .set_ext_attr = presto_set_ext_attr, #endif };