STATIC void base0fs_vm_close(vm_area_t *vma) { vm_area_t *lower_vma; file_t *file; file_t *lower_file = NULL; print_entry_location(); lower_vma = VMA_TO_LOWER(vma); file = vma->vm_file; if (FILE_TO_PRIVATE(file) != NULL) lower_file = FILE_TO_LOWER(file); BUG_ON(!lower_file); fist_dprint(6, "VM_CLOSE1: file 0x%x, lower_file 0x%x, file->f_count %d, lower_file->f_count %d\n", (int) file, (int) lower_file, (int) atomic_read(&file->f_count), (int) atomic_read(&lower_file->f_count)); BUG_ON(!lower_vma); if (lower_vma->vm_ops->close) lower_vma->vm_ops->close(lower_vma); fput(lower_file); KFREE(lower_vma); VMA_TO_LOWER(vma) = NULL; fist_dprint(6, "VM_CLOSE2: file 0x%x, lower_file 0x%x, file->f_count %d, lower_file->f_count %d\n", (int) file, (int) lower_file, (int) atomic_read(&file->f_count), (int) atomic_read(&lower_file->f_count)); print_exit_location(); }
void kdb3fs_d_release(dentry_t *dentry) { dentry_t *hidden_dentry; print_entry_location(); #if 0 fist_print_dentry("kdb3fs_d_release IN dentry", dentry); /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } hidden_dentry = dtohd(dentry); fist_print_dentry("kdb3fs_d_release IN hidden_dentry", hidden_dentry); if (hidden_dentry->d_inode) fist_dprint(6, "kdb3fs_d_release: hidden_inode->i_count %d, i_num %lu.\n", atomic_read(&hidden_dentry->d_inode->i_count), hidden_dentry->d_inode->i_ino); /* free private data (kdb3fs_dentry_info) here */ KFREE(dtopd(dentry)); dtopd(dentry) = NULL; /* just to be safe */ /* decrement hidden dentry's counter and free its inode */ dput(hidden_dentry); #endif out: print_exit_location(); }
int kdb3fs_d_delete(dentry_t *dentry) { dentry_t *hidden_dentry; int err = 0; print_entry_location(); #if 0 /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } if (!(hidden_dentry = dtohd(dentry))) { fist_dprint(6, "dentry without hidden_dentry: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } // fist_print_dentry("D_DELETE IN", dentry); /* added b/c of changes to dput(): it calls d_drop on us */ if (hidden_dentry->d_op && hidden_dentry->d_op->d_delete) { err = hidden_dentry->d_op->d_delete(hidden_dentry); } #endif out: print_exit_status(err); return err; }
STATIC void base0fs_vm_shared_unmap(vm_area_t *vma, unsigned long start, size_t len) { vm_area_t *lower_vma; file_t *file; file_t *lower_file = NULL; print_entry_location(); lower_vma = VMA_TO_LOWER(vma); file = vma->vm_file; if (FILE_TO_PRIVATE(file) != NULL) lower_file = FILE_TO_LOWER(file); BUG_ON(!lower_file); fist_dprint(6, "VM_S_UNMAP1: file 0x%x, lower_file 0x%x, file->f_count %d, lower_file->f_count %d\n", (int) file, (int) lower_file, (int) atomic_read(&file->f_count), (int) atomic_read(&lower_file->f_count)); /* * call sync (filemap_sync) because the default filemap_unmap * calls it too. */ filemap_sync(vma, start, len, MS_ASYNC); if (lower_vma->vm_ops->unmap) lower_vma->vm_ops->unmap(lower_vma, start, len); fist_dprint(6, "VM_S_UNMAP2: file 0x%x, lower_file 0x%x, file->f_count %d, lower_file->f_count %d\n", (int) file, (int) lower_file, (int) atomic_read(&file->f_count), (int) atomic_read(&lower_file->f_count)); print_exit_location(); }
/* * BKL held by caller. * dentry->d_inode->i_{sem,mutex} down */ STATIC int base0fs_removexattr(struct dentry *dentry, const char *name) { struct dentry *lower_dentry = NULL; int err = -ENOTSUPP; char *encoded_name; print_entry_location(); lower_dentry = DENTRY_TO_LOWER(dentry); BUG_ON(!lower_dentry); BUG_ON(!lower_dentry->d_inode); BUG_ON(!lower_dentry->d_inode->i_op); fist_dprint(18, "removexattr: name=\"%s\"\n", name); if (lower_dentry->d_inode->i_op->removexattr) { encoded_name = (char *)name; lock_inode(lower_dentry->d_inode); /* lock_kernel() already done by caller. */ err = lower_dentry->d_inode->i_op->removexattr(lower_dentry, encoded_name); /* unlock_kernel() will be done by caller. */ unlock_inode(lower_dentry->d_inode); } out: print_exit_status(err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down */ int unionfs_removexattr(struct dentry *dentry, const char *name) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; char *encoded_name; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_dprint(8, "removexattr: name=\"%s\"\n", name); if (hidden_dentry->d_inode->i_op->removexattr) { encoded_name = (char *)name; down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down */ int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_dprint(8, "setxattr: name=\"%s\", value %lu bytes, flags=%x\n", name, (unsigned long)size, flags); if (hidden_dentry->d_inode->i_op->setxattr) { down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op-> setxattr(hidden_dentry, name, value, size, flags); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
/* The rest of these are utility functions for lookup. */ static int is_opaque_dir(struct dentry *dentry, int bindex) { int err = 0; struct dentry *hidden_dentry; struct dentry *wh_hidden_dentry; struct inode *hidden_inode; uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; print_entry_location(); hidden_dentry = dtohd_index(dentry, bindex); hidden_inode = hidden_dentry->d_inode; BUG_ON(!S_ISDIR(hidden_inode->i_mode)); current->fsuid = hidden_inode->i_uid; current->fsgid = hidden_inode->i_gid; wh_hidden_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE, hidden_dentry, sizeof(UNIONFS_DIR_OPAQUE) - 1); current->fsuid = saved_uid; current->fsgid = saved_gid; if (IS_ERR(wh_hidden_dentry)) { err = PTR_ERR(wh_hidden_dentry); fist_dprint(1, "LOOKUP_ONE_LEN returned: %d\n", err); goto out; } if (wh_hidden_dentry->d_inode) err = 1; DPUT(wh_hidden_dentry); out: print_exit_status(err); return err; }
/* final actions when unmounting a file system */ static void unionfs_put_super(struct super_block *sb) { int bindex, bstart, bend; struct unionfs_sb_info *spd; print_entry_location(); if ((spd = stopd(sb))) { /* XXX: Free persistent inode stuff. */ bstart = sbstart(sb); bend = sbend(sb); for (bindex = bstart; bindex <= bend; bindex++) mntput(stohiddenmnt_index(sb, bindex)); /* Make sure we have no leaks of branchget/branchput. */ for (bindex = bstart; bindex <= bend; bindex++) ASSERT(branch_count(sb, bindex) == 0); KFREE(stohs_ptr(sb)); KFREE(stohiddenmnt_ptr(sb)); KFREE(spd->usi_sbcount_p); KFREE(spd->usi_branchperms_p); KFREE(spd->usi_putmaps); KFREE(spd); stopd_lhs(sb) = NULL; } fist_dprint(6, "unionfs: released super\n"); print_exit_location(); }
void unionfs_d_release(struct dentry *dentry) { struct dentry *hidden_dentry; int bindex, bstart, bend; print_entry_location(); /* There is no reason to lock the dentry, because we have the only * reference, but the printing functions verify that we have a lock * on the dentry before calling dbstart, etc. */ lock_dentry(dentry); __fist_print_dentry("unionfs_d_release IN dentry", dentry, 0); /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } else if (dbstart(dentry) < 0) { /* this is due to a failed lookup */ /* the failed lookup has a dtohd_ptr set to null, but this is a better check */ fist_dprint(6, "dentry without hidden dentries : %*s", dentry->d_name.len, dentry->d_name.name); goto out_free; } /* Release all the hidden dentries */ bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); DPUT(hidden_dentry); set_dtohd_index(dentry, bindex, NULL); } /* free private data (unionfs_dentry_info) here */ KFREE(dtohd_ptr(dentry)); dtohd_ptr(dentry) = NULL; out_free: /* No need to unlock it, because it is disappeared. */ #ifdef TRACKLOCK printk("DESTROYLOCK:%p\n", dentry); #endif free_dentry_private_data(dtopd(dentry)); dtopd_lhs(dentry) = NULL; /* just to be safe */ out: print_exit_location(); }
STATIC void base0fs_vm_open(vm_area_t *vma) { vm_area_t *lower_vma, *lower_vma2; file_t *file; file_t *lower_file = NULL; print_entry_location(); lower_vma = VMA_TO_LOWER(vma); file = vma->vm_file; if (FILE_TO_PRIVATE(file) != NULL) lower_file = FILE_TO_LOWER(file); BUG_ON(!lower_file); fist_dprint(6, "VM_OPEN: file 0x%x, lower_file 0x%x, file->f_count %d, lower_file->f_count %d\n", (int) file, (int) lower_file, (int) atomic_read(&file->f_count), (int) atomic_read(&lower_file->f_count)); if (lower_vma->vm_ops->open) lower_vma->vm_ops->open(lower_vma); atomic_inc(&lower_file->f_count); /* we need to duplicate the private data */ lower_vma2 = KMALLOC(sizeof(vm_area_t), GFP_KERNEL); if (!lower_vma2) { printk("VM_OPEN: Out of memory\n"); goto out; } memcpy(lower_vma2, lower_vma, sizeof(vm_area_t)); VMA_TO_LOWER(vma) = lower_vma2; out: fist_dprint(6, "VM_OPEN2: file 0x%x, lower_file 0x%x, file->f_count %d, lower_file->f_count %d\n", (int) file, (int) lower_file, (int) atomic_read(&file->f_count), (int) atomic_read(&lower_file->f_count)); print_exit_location(); }
static void unionfs_read_inode(struct inode *inode) { static struct address_space_operations unionfs_empty_aops; print_entry_location(); if (!itopd(inode)) { FISTBUG ("No kernel memory when allocating inode private data!\n"); } PASSERT(inode->i_sb); memset(itopd(inode), 0, sizeof(struct unionfs_inode_info)); itopd(inode)->b_start = -1; itopd(inode)->b_end = -1; atomic_set(&itopd(inode)->uii_generation, atomic_read(&stopd(inode->i_sb)->usi_generation)); itopd(inode)->uii_rdlock = SPIN_LOCK_UNLOCKED; itopd(inode)->uii_rdcount = 1; itopd(inode)->uii_hashsize = -1; INIT_LIST_HEAD(&itopd(inode)->uii_readdircache); if (sbmax(inode->i_sb) > UNIONFS_INLINE_OBJECTS) { int size = (sbmax(inode->i_sb) - UNIONFS_INLINE_OBJECTS) * sizeof(struct inode *); itohi_ptr(inode) = KMALLOC(size, GFP_UNIONFS); if (!itohi_ptr(inode)) { FISTBUG ("No kernel memory when allocating lower-pointer array!\n"); } memset(itohi_ptr(inode), 0, size); } memset(itohi_inline(inode), 0, UNIONFS_INLINE_OBJECTS * sizeof(struct inode *)); inode->i_version++; inode->i_op = &unionfs_main_iops; inode->i_fop = &unionfs_main_fops; /* I don't think ->a_ops is ever allowed to be NULL */ inode->i_mapping->a_ops = &unionfs_empty_aops; fist_dprint(7, "setting inode 0x%p a_ops to empty (0x%p)\n", inode, inode->i_mapping->a_ops); print_exit_location(); }
static void unionfs_put_inode(struct inode *inode) { print_entry_location(); fist_dprint(8, "%s i_count = %d, i_nlink = %d\n", __FUNCTION__, atomic_read(&inode->i_count), inode->i_nlink); /* * This is really funky stuff: * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed. * It is currently holding a reference to the hidden inode. * Therefore, it needs to release that reference by calling iput on the hidden inode. * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0. * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files. * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode. */ if (atomic_read(&inode->i_count) == 1) inode->i_nlink = 0; print_exit_location(); }
static loff_t unionfs_llseek(struct file *file, loff_t offset, int origin) { loff_t err; struct file *hidden_file = NULL; print_entry_location(); fist_dprint(6, "unionfs_llseek: file=%p, offset=0x%llx, origin=%d\n", file, offset, origin); if ((err = unionfs_file_revalidate(file, 0))) goto out; PASSERT(ftopd(file)); hidden_file = ftohf(file); PASSERT(hidden_file); /* always set hidden position to this one */ hidden_file->f_pos = file->f_pos; memcpy(&(hidden_file->f_ra), &(file->f_ra), sizeof(struct file_ra_state)); if (hidden_file->f_op && hidden_file->f_op->llseek) err = hidden_file->f_op->llseek(hidden_file, offset, origin); else err = generic_file_llseek(hidden_file, offset, origin); if (err < 0) goto out; if (err != file->f_pos) { file->f_pos = err; // ION maybe this? // file->f_pos = hidden_file->f_pos; file->f_version++; } out: print_exit_status((int)err); return err; }
/* BKL held by caller. * dentry->d_inode->i_sem down * ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); */ ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct dentry *hidden_dentry = NULL; int err = -EOPNOTSUPP; /* Define these anyway so we don't need as much ifdef'ed code. */ char *encoded_name = NULL; char *encoded_value = NULL; print_entry_location(); lock_dentry(dentry); hidden_dentry = dtohd(dentry); fist_dprint(8, "getxattr: name=\"%s\", value %lu bytes\n", name, (unsigned long)size); if (hidden_dentry->d_inode->i_op->getxattr) { encoded_name = (char *)name; encoded_value = (char *)value; down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } unlock_dentry(dentry); print_exit_status(err); return err; }
/* * BKL held by caller. * dentry->d_inode->i_{sem,mutex} down */ STATIC ssize_t base0fs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct dentry *lower_dentry = NULL; int err = -ENOTSUPP; /* Define these anyway so we don't need as much ifdef'ed code. */ char *encoded_name = NULL; char *encoded_value = NULL; print_entry_location(); lower_dentry = DENTRY_TO_LOWER(dentry); BUG_ON(!lower_dentry); BUG_ON(!lower_dentry->d_inode); BUG_ON(!lower_dentry->d_inode->i_op); fist_dprint(18, "getxattr: name=\"%s\", value %d bytes\n", name, (int) size); if (lower_dentry->d_inode->i_op->getxattr) { encoded_name = (char *)name; encoded_value = (char *)value; lock_inode(lower_dentry->d_inode); /* lock_kernel() already done by caller. */ err = lower_dentry->d_inode->i_op->getxattr(lower_dentry, encoded_name, encoded_value, size); /* unlock_kernel() will be done by caller. */ unlock_inode(lower_dentry->d_inode); } out: print_exit_status(err); return err; }
static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { int err = 0; struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL; struct dentry *hidden_parent_dentry = NULL; int bindex = 0, bstart; char *name = NULL; int whiteout_unlinked = 0; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_mknod", dentry); bstart = dbstart(dentry); hidden_dentry = dtohd(dentry); // check if whiteout exists in this branch, i.e. lookup .wh.foo first name = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, dentry->d_name.len + WHLEN); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { /* found .wh.foo, unlink it */ hidden_parent_dentry = lock_parent(whiteout_dentry); //found a.wh.foo entry, remove it then do vfs_mkdir if (!(err = is_robranch_super(dentry->d_sb, bstart))) err = vfs_unlink(hidden_parent_dentry->d_inode, whiteout_dentry); DPUT(whiteout_dentry); unlock_dir(hidden_parent_dentry); if (err) { if (!IS_COPYUP_ERR(err)) goto out; bstart--; } else { whiteout_unlinked = 1; } } for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { hidden_dentry = create_parents(dir, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex); continue; } } hidden_parent_dentry = lock_parent(hidden_dentry); if (IS_ERR(hidden_parent_dentry)) { err = PTR_ERR(hidden_parent_dentry); goto out; } if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_mknod(hidden_parent_dentry->d_inode, hidden_dentry, mode, dev); } /* XXX this could potentially return a negative hidden_dentry! */ if (err || !hidden_dentry->d_inode) { unlock_dir(hidden_parent_dentry); /* break out of for, if error was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { err = unionfs_interpose(dentry, dir->i_sb, 0); if (!err) { fist_copy_attr_timesizes(dir, hidden_parent_dentry-> d_inode); /* update number of links on parent directory */ dir->i_nlink = get_nlinks(dir); } unlock_dir(hidden_parent_dentry); break; } } out: if (!dentry->d_inode) d_drop(dentry); if (name) { KFREE(name); } fist_print_dentry("OUT unionfs_mknod :", dentry); unlock_dentry(dentry); print_exit_status(err); return err; }
static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info *hidden_root_info, char *options) { struct nameidata nd; char *name; int err = 0; int branches = 1; int bindex = 0; int i = 0; int j = 0; struct dentry *dent1 = NULL; struct dentry *dent2 = NULL; if (options[0] == '\0') { printk(KERN_WARNING "unionfs: no branches specified\n"); err = -EINVAL; goto out; } /* Each colon means we have a separator, this is really just a rough * guess, since strsep will handle empty fields for us. */ for (i = 0; options[i]; i++) { if (options[i] == ':') branches++; } /* allocate space for underlying pointers to hidden dentry */ if (!(stopd(sb)->usi_data = alloc_new_data(branches))) { err = -ENOMEM; goto out; } if (!(hidden_root_info->udi_dentry = alloc_new_dentries(branches))) { err = -ENOMEM; goto out; } /* now parsing the string b1:b2=rw:b3=ro:b4 */ branches = 0; while ((name = strsep(&options, ":")) != NULL) { int perms; if (!*name) continue; branches++; /* strip off =rw or =ro if it is specified. */ perms = parse_branch_mode(name); if (!bindex && !(perms & MAY_WRITE)) { err = -EINVAL; goto out; } fist_dprint(4, "unionfs: using directory: %s (%c%c%c)\n", name, perms & MAY_READ ? 'r' : '-', perms & MAY_WRITE ? 'w' : '-', perms & MAY_NFSRO ? 'n' : '-'); err = path_lookup(name, LOOKUP_FOLLOW, &nd); RECORD_PATH_LOOKUP(&nd); if (err) { printk(KERN_WARNING "unionfs: error accessing " "hidden directory '%s' (error %d)\n", name, err); goto out; } if ((err = check_branch(&nd))) { printk(KERN_WARNING "unionfs: hidden directory " "'%s' is not a valid branch\n", name); path_release(&nd); RECORD_PATH_RELEASE(&nd); goto out; } hidden_root_info->udi_dentry[bindex] = nd.dentry; set_stohiddenmnt_index(sb, bindex, nd.mnt); set_branchperms(sb, bindex, perms); set_branch_count(sb, bindex, 0); if (hidden_root_info->udi_bstart < 0) hidden_root_info->udi_bstart = bindex; hidden_root_info->udi_bend = bindex; bindex++; } if (branches == 0) { printk(KERN_WARNING "unionfs: no branches specified\n"); err = -EINVAL; goto out; } BUG_ON(branches != (hidden_root_info->udi_bend + 1)); /* ensure that no overlaps exist in the branches */ for (i = 0; i < branches; i++) { for (j = i + 1; j < branches; j++) { dent1 = hidden_root_info->udi_dentry[i]; dent2 = hidden_root_info->udi_dentry[j]; if (is_branch_overlap(dent1, dent2)) { goto out_overlap; } } } out_overlap: if (i != branches) { printk(KERN_WARNING "unionfs: branches %d and %d overlap\n", i, j); err = -EINVAL; goto out; } out: if (err) { for (i = 0; i < branches; i++) { if (hidden_root_info->udi_dentry[i]) DPUT(hidden_root_info->udi_dentry[i]); } KFREE(hidden_root_info->udi_dentry); KFREE(stopd(sb)->usi_data); /* MUST clear the pointers to prevent potential double free if * the caller dies later on */ hidden_root_info->udi_dentry = NULL; stopd(sb)->usi_data = NULL; } return err; }
STATIC int base0fs_mmap(file_t *file, vm_area_t *vma) { int err = 0; file_t *lower_file = NULL; inode_t *inode; inode_t *lower_inode; vm_area_t *lower_vma = (vm_area_t *) 0xdeadc0de; print_entry_location(); if (FILE_TO_PRIVATE(file) != NULL) lower_file = FILE_TO_LOWER(file); BUG_ON(!lower_file); inode = file->f_dentry->d_inode; lower_inode = INODE_TO_LOWER(inode); fist_dprint(6, "MMAP1: inode 0x%x, lower_inode 0x%x, inode->i_count %d, lower_inode->i_count %d\n", (int) inode, (int) lower_inode, atomic_read(&inode->i_count), atomic_read(&lower_inode->i_count)); if (!lower_file->f_op || !lower_file->f_op->mmap) { err = -ENODEV; goto out; } /* * Most of this code comes straight from generic_file_mmap * in mm/filemap.c. */ if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) { vma->vm_ops = &base0fs_shared_vmops; } else { vma->vm_ops = &base0fs_private_vmops; } if (!inode->i_sb || !S_ISREG(inode->i_mode)) { err = -EACCES; goto out; } if (!lower_inode->i_op || !lower_inode->i_mapping->a_ops->readpage) { err = -ENOEXEC; goto out; } UPDATE_ATIME(inode); /* * Now we do the lower stuff, but only for shared maps. */ if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) { lower_vma = KMALLOC(sizeof(vm_area_t), GFP_KERNEL); if (!lower_vma) { printk("MMAP: Out of memory\n"); err = -ENOMEM; goto out; } // ION, is this right? memcpy(lower_vma, vma, sizeof(vm_area_t)); VMA_TO_LOWER(vma) = lower_vma; err = lower_file->f_op->mmap(lower_file, lower_vma); lower_vma->vm_file = lower_file; atomic_inc(&lower_file->f_count); } /* * XXX: do we need to insert_vm_struct and merge_segments as is * done in do_mmap()? */ out: fist_dprint(6, "MMAP2: inode 0x%x, lower_inode 0x%x, inode->i_count %d, lower_inode->i_count %d\n", (int) inode, (int) lower_inode, atomic_read(&inode->i_count), atomic_read(&lower_inode->i_count)); #if 0 if (!err) { inode->i_mmap = vma; lower_inode->i_mmap = lower_vma; } #endif print_exit_status(err); return err; }
/* sb we pass is unionfs's super_block */ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag) { struct inode *hidden_inode; struct dentry *hidden_dentry; int err = 0; struct inode *inode; int is_negative_dentry = 1; int bindex, bstart, bend; print_entry("flag = %d", flag); verify_locked(dentry); fist_print_dentry("In unionfs_interpose", dentry); bstart = dbstart(dentry); bend = dbend(dentry); /* Make sure that we didn't get a negative dentry. */ for (bindex = bstart; bindex <= bend; bindex++) { if (dtohd_index(dentry, bindex) && dtohd_index(dentry, bindex)->d_inode) { is_negative_dentry = 0; break; } } BUG_ON(is_negative_dentry); /* We allocate our new inode below, by calling iget. * iget will call our read_inode which will initialize some * of the new inode's fields */ /* On revalidate we've already got our own inode and just need * to fix it up. */ if (flag == INTERPOSE_REVAL) { inode = dentry->d_inode; itopd(inode)->b_start = -1; itopd(inode)->b_end = -1; atomic_set(&itopd(inode)->uii_generation, atomic_read(&stopd(sb)->usi_generation)); itohi_ptr(inode) = KZALLOC(sbmax(sb) * sizeof(struct inode *), GFP_KERNEL); if (!itohi_ptr(inode)) { err = -ENOMEM; goto out; } } else { ino_t ino; /* get unique inode number for unionfs */ #ifdef UNIONFS_IMAP if (stopd(sb)->usi_persistent) { err = read_uin(sb, bindex, dtohd_index(dentry, bindex)->d_inode->i_ino, O_CREAT, &ino); if (err) goto out; } else #endif ino = iunique(sb, UNIONFS_ROOT_INO); inode = IGET(sb, ino); if (!inode) { err = -EACCES; /* should be impossible??? */ goto out; } } down(&inode->i_sem); if (atomic_read(&inode->i_count) > 1) goto skip; for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { set_itohi_index(inode, bindex, NULL); continue; } /* Initialize the hidden inode to the new hidden inode. */ if (!hidden_dentry->d_inode) continue; set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); /* Use attributes from the first branch. */ hidden_inode = itohi(inode); /* Use different set of inode ops for symlinks & directories */ if (S_ISLNK(hidden_inode->i_mode)) inode->i_op = &unionfs_symlink_iops; else if (S_ISDIR(hidden_inode->i_mode)) inode->i_op = &unionfs_dir_iops; /* Use different set of file ops for directories */ if (S_ISDIR(hidden_inode->i_mode)) inode->i_fop = &unionfs_dir_fops; /* properly initialize special inodes */ if (S_ISBLK(hidden_inode->i_mode) || S_ISCHR(hidden_inode->i_mode) || S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode)) init_special_inode(inode, hidden_inode->i_mode, hidden_inode->i_rdev); /* Fix our inode's address operations to that of the lower inode (Unionfs is FiST-Lite) */ if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops) { fist_dprint(7, "fixing inode 0x%p a_ops (0x%p -> 0x%p)\n", inode, inode->i_mapping->a_ops, hidden_inode->i_mapping->a_ops); inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops; } /* all well, copy inode attributes */ fist_copy_attr_all(inode, hidden_inode); skip: /* only (our) lookup wants to do a d_add */ switch (flag) { case INTERPOSE_DEFAULT: case INTERPOSE_REVAL_NEG: d_instantiate(dentry, inode); break; case INTERPOSE_LOOKUP: err = PTR_ERR(d_splice_alias(inode, dentry)); break; case INTERPOSE_REVAL: /* Do nothing. */ break; default: printk(KERN_ERR "Invalid interpose flag passed!"); BUG(); } fist_print_dentry("Leaving unionfs_interpose", dentry); fist_print_inode("Leaving unionfs_interpose", inode); up(&inode->i_sem); out: print_exit_status(err); return err; }
STATIC struct dentry * base0fs_lookup(inode_t *dir, struct dentry *dentry, struct nameidata* nd_unused_in_this_fxn /* XXX: fix code if ever used */ ) { int err = 0; struct dentry *lower_dir_dentry; struct dentry *lower_dentry = NULL; struct vfsmount *lower_mount; const char *name; vnode_t *this_vnode; struct dentry *this_dir; unsigned int namelen; print_entry_location(); lower_dir_dentry = base0fs_lower_dentry(dentry->d_parent); /* CPW: Moved below print_entry_location */ name = dentry->d_name.name; namelen = dentry->d_name.len; fist_checkinode(dir, "base0fs_lookup"); this_vnode = dir; this_dir = lower_dir_dentry; fist_print_dentry("base0fs_lookup IN", dentry); fist_print_dentry("base0fs_lookup: dentry->d_parent IN", dentry->d_parent); fist_print_dentry("base0fs_lookup: lower_dir_dentry IN", lower_dir_dentry); fist_print_inode("base0fs_lookup: dir IN", dir); if (lower_dir_dentry->d_inode) fist_print_inode("base0fs_lookup: lower_dir_dentry->d_inode", lower_dir_dentry->d_inode); /* must initialize dentry operations */ dentry->d_op = &base0fs_dops; ; /* increase refcount of base dentry (lookup_one[_len] will decrement) */ // THIS IS RIGHT! (don't "fix" it) // NO THIS IS WRONG IN 2.3.99-pre6. lookup_one[_len] will NOT decrement // dget(lower_dir_dentry); lock_inode(lower_dir_dentry->d_inode); /* will allocate a new lower dentry if needed */ lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen); unlock_inode(lower_dir_dentry->d_inode); if (IS_ERR(lower_dentry)) { /* * this produces an unusual dentry: one that has neither an * inode, nor a private structure attached to it. All cleanup * methods (d_delete, d_release, etc) must be prepared to deal * with such dentries. Ion 09/29/2001 */ err = PTR_ERR(lower_dentry); goto out; } lower_mount = mntget(DENTRY_TO_LVFSMNT(dentry->d_parent)); ; if (lower_dentry->d_inode && lower_dentry->d_inode->i_sb) { struct super_block *lower_sb = lower_dentry->d_inode->i_sb; if (lower_sb->s_dev == sProcDev || lower_sb->s_dev == sDevDev) { d_drop(dentry); /* Don't leak our dentry. */ return lower_dentry; } } /* update parent directory's atime */ fist_copy_attr_atime(dir, lower_dir_dentry->d_inode); /* link the upper and lower dentries */ DENTRY_TO_PRIVATE_SM(dentry) = (struct base0fs_dentry_info *) KMALLOC(sizeof(struct base0fs_dentry_info), GFP_KERNEL); if (!DENTRY_TO_PRIVATE(dentry)) { err = -ENOMEM; goto out_dput; } DENTRY_TO_PRIVATE(dentry)->wdi_dentry = lower_dentry; DENTRY_TO_PRIVATE(dentry)->wdi_mnt = lower_mount; /* lookup is special: it needs to handle negative dentries */ if (!lower_dentry->d_inode) { d_add(dentry, NULL); fist_print_dentry("base0fs_lookup OUT lower_dentry", lower_dentry); goto out; } fist_dprint(6, "lookup \"%s\" -> inode %lu\n", name, lower_dentry->d_inode->i_ino); err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 1); if (err) goto out_free; fist_checkinode(dentry->d_inode, "base0fs_lookup OUT: dentry->d_inode:"); fist_checkinode(dir, "base0fs_lookup OUT: dir"); fist_print_dentry("base0fs_lookup OUT lower_dentry", lower_dentry); fist_print_inode("base0fs_lookup OUT lower_inode", lower_dentry->d_inode); /* All is well */ goto out; out_free: d_drop(dentry); /* so that our bad dentry will get destroyed */ KFREE(DENTRY_TO_PRIVATE(dentry)); DENTRY_TO_PRIVATE_SM(dentry) = NULL; /* be safe */ out_dput: if (lower_dentry) dput(lower_dentry); out: fist_print_dentry("base0fs_lookup OUT", dentry); print_exit_status(err); return ERR_PTR(err); }
static int unionfs_rmdir_all(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bstart, bend, bindex; int err = 0; int global_err = 0; print_entry_location(); fist_print_dentry("IN unionfs_rmdir_all: ", dentry); err = unionfs_partial_lookup(dentry); if (err) { fist_dprint(8, "Error in partial lookup\n"); goto out; } bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bend; bindex >= bstart; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; hidden_dir_dentry = lock_parent(hidden_dentry); if (S_ISDIR(hidden_dentry->d_inode->i_mode)) { delete_whiteouts(dentry, bindex, namelist); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); } } else { err = -EISDIR; } fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); unlock_dir(hidden_dir_dentry); if (err) { int local_err = unionfs_refresh_hidden_dentry(dentry, bindex); if (local_err) { err = local_err; goto out; } if (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY && err != -EISDIR) goto out; global_err = err; } } /* check if encountered error in the above loop */ if (global_err) { /* If we failed in the leftmost branch, then err will be set and we should * move one over to create the whiteout. Otherwise, we should try in the * leftmost branch. */ if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else { err = create_whiteout(dentry, dbstart(dentry)); } } out: /* propagate number of hard-links */ dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); fist_print_dentry("OUT unionfs_rmdir_all: ", dentry); print_exit_status(err); return err; }
static int unionfs_unlink_all(struct inode *dir, struct dentry *dentry) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bstart, bend, bindex; int err = 0; int global_err = 0; print_entry_location(); bstart = dbstart(dentry); err = unionfs_partial_lookup(dentry); if (err) { fist_dprint(8, "Error in partial lookup\n"); goto out; } bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bend; bindex >= bstart; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ DGET(hidden_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry); } DPUT(hidden_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); if (err) { if (!IS_COPYUP_ERR(err)) { /* passup the last error we got */ unlock_dir(hidden_dir_dentry); goto out; } global_err = err; } unlock_dir(hidden_dir_dentry); } /* check if encountered error in the above loop */ if (global_err) { /* If we failed in the leftmost branch, then err will be set * and we should move one over to create the whiteout. * Otherwise, we should try in the leftmost branch. */ if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else { err = create_whiteout(dentry, dbstart(dentry)); } } else if (dbopaque(dentry) != -1) { /* There is a hidden lower-priority file with the same name. */ err = create_whiteout(dentry, dbopaque(dentry)); } out: /* propagate number of hard-links */ if (dentry->d_inode->i_nlink != 0) { dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); if (!err && global_err) dentry->d_inode->i_nlink--; } /* We don't want to leave negative leftover dentries for revalidate. */ if (!err && (global_err || dbopaque(dentry) != -1)) update_bstart(dentry); print_exit_status(err); return err; }
static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode) { int err = 0; struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL; struct dentry *hidden_parent_dentry = NULL; int bindex = 0, bstart; char *name = NULL; int whiteout_unlinked = 0; uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_mkdir", dentry); bstart = dbstart(dentry); hidden_dentry = dtohd(dentry); // check if whiteout exists in this branch, i.e. lookup .wh.foo first name = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, dentry->d_name.len + WHLEN); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { hidden_parent_dentry = lock_parent(whiteout_dentry); /* Set the uid and gid to trick the fs into allowing us to create * the file */ current->fsuid = hidden_parent_dentry->d_inode->i_uid; current->fsgid = hidden_parent_dentry->d_inode->i_gid; //found a.wh.foo entry, remove it then do vfs_mkdir if (!(err = is_robranch_super(dentry->d_sb, bstart))) { err = vfs_unlink(hidden_parent_dentry->d_inode, whiteout_dentry); } DPUT(whiteout_dentry); current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(hidden_parent_dentry); if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; bstart--; } else { whiteout_unlinked = 1; } } for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { hidden_dentry = create_parents(parent, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex); continue; } } hidden_parent_dentry = lock_parent(hidden_dentry); if (IS_ERR(hidden_parent_dentry)) { err = PTR_ERR(hidden_parent_dentry); goto out; } if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, mode); } unlock_dir(hidden_parent_dentry); /* XXX this could potentially return a negative hidden_dentry! */ if (err || !hidden_dentry->d_inode) { /* break out of for loop if error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { int i; int bend = dbend(dentry); for (i = bindex + 1; i < bend; i++) { if (dtohd_index(dentry, i)) { DPUT(dtohd_index(dentry, i)); set_dtohd_index(dentry, i, NULL); } } bend = bindex; set_dbend(dentry, bend); err = unionfs_interpose(dentry, parent->i_sb, 0); if (!err) { fist_copy_attr_timesizes(parent, hidden_parent_dentry-> d_inode); /* update number of links on parent directory */ parent->i_nlink = get_nlinks(parent); } whiteout_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE, hidden_dentry, sizeof (UNIONFS_DIR_OPAQUE) - 1); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } down(&hidden_dentry->d_inode->i_sem); err = vfs_create(hidden_dentry->d_inode, whiteout_dentry, 0600, NULL); up(&hidden_dentry->d_inode->i_sem); DPUT(whiteout_dentry); if (err) { fist_dprint(8, "mkdir: error creating directory override entry: %d\n", err); goto out; } break; } } out: if (!dentry->d_inode) d_drop(dentry); KFREE(name); fist_print_dentry("OUT unionfs_mkdir :", dentry); unlock_dentry(dentry); print_exit_status(err); return err; }
static int unionfs_statfs(struct super_block *sb, struct kstatfs *buf) { int err = 0; struct super_block *hidden_sb; struct kstatfs rsb; int bindex, bindex1, bstart, bend; print_entry_location(); memset(buf, 0, sizeof(struct kstatfs)); buf->f_type = UNIONFS_SUPER_MAGIC; buf->f_frsize = 0; buf->f_namelen = 0; bstart = sbstart(sb); bend = sbend(sb); for (bindex = bstart; bindex <= bend; bindex++) { int dup = 0; hidden_sb = stohs_index(sb, bindex); /* Ignore duplicate super blocks. */ for (bindex1 = bstart; bindex1 < bindex; bindex1++) { if (hidden_sb == stohs_index(sb, bindex1)) { dup = 1; break; } } if (dup) { continue; } err = vfs_statfs(hidden_sb, &rsb); fist_dprint(8, "adding values for bindex:%d bsize:%d blocks:%d bfree:%d bavail:%d\n", bindex, (int)rsb.f_bsize, (int)rsb.f_blocks, (int)rsb.f_bfree, (int)rsb.f_bavail); if (!buf->f_frsize) buf->f_frsize = rsb.f_frsize; if (!buf->f_namelen) { buf->f_namelen = rsb.f_namelen; } else { if (buf->f_namelen > rsb.f_namelen) buf->f_namelen = rsb.f_namelen; } if (!buf->f_bsize) { buf->f_bsize = rsb.f_bsize; } else { if (buf->f_bsize < rsb.f_bsize) { int shifter = 0; while (buf->f_bsize < rsb.f_bsize) { shifter++; rsb.f_bsize >>= 1; } rsb.f_blocks <<= shifter; rsb.f_bfree <<= shifter; rsb.f_bavail <<= shifter; } else { int shifter = 0; while (buf->f_bsize > rsb.f_bsize) { shifter++; rsb.f_bsize <<= 1; } rsb.f_blocks >>= shifter; rsb.f_bfree >>= shifter; rsb.f_bavail >>= shifter; } }
struct dentry *unionfs_lookup_backend(struct dentry *dentry, int lookupmode) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *wh_hidden_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; struct dentry *parent_dentry = NULL; int bindex, bstart, bend, bopaque; int dentry_count = 0; /* Number of positive dentries. */ int first_dentry_offset = -1; struct dentry *first_hidden_dentry = NULL; int locked_parent = 0; int locked_child = 0; int opaque; char *whname = NULL; const char *name; int namelen; print_entry("mode = %d", lookupmode); /* We should already have a lock on this dentry in the case of a * partial lookup, or a revalidation. Otherwise it is returned from * new_dentry_private_data already locked. */ if (lookupmode == INTERPOSE_PARTIAL || lookupmode == INTERPOSE_REVAL || lookupmode == INTERPOSE_REVAL_NEG) { verify_locked(dentry); } else { BUG_ON(dtopd_nocheck(dentry) != NULL); locked_child = 1; } if (lookupmode != INTERPOSE_PARTIAL) if ((err = new_dentry_private_data(dentry))) goto out; /* must initialize dentry operations */ dentry->d_op = &unionfs_dops; parent_dentry = GET_PARENT(dentry); /* We never partial lookup the root directory. */ if (parent_dentry != dentry) { lock_dentry(parent_dentry); locked_parent = 1; } else { DPUT(parent_dentry); parent_dentry = NULL; goto out; } fist_print_dentry("IN unionfs_lookup (parent)", parent_dentry); fist_print_dentry("IN unionfs_lookup (child)", dentry); name = dentry->d_name.name; namelen = dentry->d_name.len; /* No dentries should get created for possible whiteout names. */ if (!is_validname(name)) { err = -EPERM; goto out_free; } /* Now start the actual lookup procedure. */ bstart = dbstart(parent_dentry); bend = dbend(parent_dentry); bopaque = dbopaque(parent_dentry); BUG_ON(bstart < 0); /* It would be ideal if we could convert partial lookups to only have * to do this work when they really need to. It could probably improve * performance quite a bit, and maybe simplify the rest of the code. */ if (lookupmode == INTERPOSE_PARTIAL) { bstart++; if ((bopaque != -1) && (bopaque < bend)) bend = bopaque; } fist_dprint(8, "bstart = %d, bend = %d\n", bstart, bend); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry) continue; BUG_ON(hidden_dentry != NULL); hidden_dir_dentry = dtohd_index(parent_dentry, bindex); /* if the parent hidden dentry does not exist skip this */ if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode)) continue; /* also skip it if the parent isn't a directory. */ if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode)) continue; /* Reuse the whiteout name because its value doesn't change. */ if (!whname) { whname = alloc_whname(name, namelen); if (IS_ERR(whname)) { err = PTR_ERR(whname); goto out_free; } } /* check if whiteout exists in this branch: lookup .wh.foo */ wh_hidden_dentry = LOOKUP_ONE_LEN(whname, hidden_dir_dentry, namelen + WHLEN); if (IS_ERR(wh_hidden_dentry)) { DPUT(first_hidden_dentry); err = PTR_ERR(wh_hidden_dentry); goto out_free; } if (wh_hidden_dentry->d_inode) { /* We found a whiteout so lets give up. */ fist_dprint(8, "whiteout found in %d\n", bindex); if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) { set_dbend(dentry, bindex); set_dbopaque(dentry, bindex); DPUT(wh_hidden_dentry); break; } err = -EIO; printk(KERN_NOTICE "EIO: Invalid whiteout entry type" " %d.\n", wh_hidden_dentry->d_inode->i_mode); DPUT(wh_hidden_dentry); DPUT(first_hidden_dentry); goto out_free; } DPUT(wh_hidden_dentry); wh_hidden_dentry = NULL; /* Now do regular lookup; lookup foo */ hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry, namelen); fist_print_generic_dentry("hidden result", hidden_dentry); if (IS_ERR(hidden_dentry)) { DPUT(first_hidden_dentry); err = PTR_ERR(hidden_dentry); goto out_free; } /* Store the first negative dentry specially, because if they * are all negative we need this for future creates. */ if (!hidden_dentry->d_inode) { if (!first_hidden_dentry && (dbstart(dentry) == -1)) { first_hidden_dentry = hidden_dentry; first_dentry_offset = bindex; } else { DPUT(hidden_dentry); } continue; } /* number of positive dentries */ dentry_count++; /* store underlying dentry */ if (dbstart(dentry) == -1) set_dbstart(dentry, bindex); set_dtohd_index(dentry, bindex, hidden_dentry); set_dbend(dentry, bindex); /* update parent directory's atime with the bindex */ fist_copy_attr_atime(parent_dentry->d_inode, hidden_dir_dentry->d_inode); /* We terminate file lookups here. */ if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) { if (lookupmode == INTERPOSE_PARTIAL) continue; if (dentry_count == 1) goto out_positive; /* This can only happen with mixed D-*-F-* */ BUG_ON(!S_ISDIR(dtohd(dentry)->d_inode->i_mode)); continue; } opaque = is_opaque_dir(dentry, bindex); if (opaque < 0) { DPUT(first_hidden_dentry); err = opaque; goto out_free; } if (opaque) { set_dbend(dentry, bindex); set_dbopaque(dentry, bindex); break; } } if (dentry_count) goto out_positive; else goto out_negative; out_negative: if (lookupmode == INTERPOSE_PARTIAL) goto out; /* If we've only got negative dentries, then use the leftmost one. */ if (lookupmode == INTERPOSE_REVAL) { if (dentry->d_inode) { itopd(dentry->d_inode)->uii_stale = 1; } goto out; } /* This should only happen if we found a whiteout. */ if (first_dentry_offset == -1) { first_hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry, namelen); first_dentry_offset = bindex; if (IS_ERR(first_hidden_dentry)) { err = PTR_ERR(first_hidden_dentry); goto out; } } set_dtohd_index(dentry, first_dentry_offset, first_hidden_dentry); set_dbstart(dentry, first_dentry_offset); set_dbend(dentry, first_dentry_offset); if (lookupmode == INTERPOSE_REVAL_NEG) BUG_ON(dentry->d_inode != NULL); else d_add(dentry, NULL); goto out; /* This part of the code is for positive dentries. */ out_positive: BUG_ON(dentry_count <= 0); /* If we're holding onto the first negative dentry throw it out. */ DPUT(first_hidden_dentry); /* Partial lookups need to reinterpose, or throw away older negs. */ if (lookupmode == INTERPOSE_PARTIAL) { if (dentry->d_inode) { unionfs_reinterpose(dentry); goto out; } /* This somehow turned positive, so it is as if we had a * negative revalidation. */ lookupmode = INTERPOSE_REVAL_NEG; update_bstart(dentry); bstart = dbstart(dentry); bend = dbend(dentry); } err = unionfs_interpose(dentry, dentry->d_sb, lookupmode); if (err) goto out_drop; fist_checkinode(dentry->d_inode, "unionfs_lookup OUT: child"); fist_checkinode(parent_dentry->d_inode, "unionfs_lookup OUT: dir"); goto out; out_drop: d_drop(dentry); out_free: /* should dput all the underlying dentries on error condition */ bstart = dbstart(dentry); if (bstart >= 0) { bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) DPUT(dtohd_index(dentry, bindex)); } KFREE(dtohd_ptr(dentry)); dtohd_ptr(dentry) = NULL; set_dbstart(dentry, -1); set_dbend(dentry, -1); out: if (!err && dtopd(dentry)) { BUG_ON(dbend(dentry) > dtopd(dentry)->udi_bcount); BUG_ON(dbend(dentry) > sbmax(dentry->d_sb)); BUG_ON(dbstart(dentry) < 0); } KFREE(whname); fist_print_dentry("OUT unionfs_lookup (parent)", parent_dentry); fist_print_dentry("OUT unionfs_lookup (child)", dentry); if (locked_parent) unlock_dentry(parent_dentry); DPUT(parent_dentry); if (locked_child) unlock_dentry(dentry); print_exit_status(err); return ERR_PTR(err); }
/* * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. */ int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid = 1; /* default is valid (1); invalid is 0. */ struct dentry *hidden_dentry; int bindex, bstart, bend; int sbgen, dgen; int positive = 0; int locked = 0; int restart = 0; int interpose_flag; print_util_entry_location(); restart: verify_locked(dentry); /* if the dentry is unhashed, do NOT revalidate */ if (d_deleted(dentry)) { fist_dprint(6, "unhashed dentry being revalidated: %*s\n", dentry->d_name.len, dentry->d_name.name); goto out; } BUG_ON(dbstart(dentry) == -1); if (dentry->d_inode) positive = 1; dgen = atomic_read(&dtopd(dentry)->udi_generation); sbgen = atomic_read(&stopd(dentry->d_sb)->usi_generation); /* If we are working on an unconnected dentry, then there is no * revalidation to be done, because this file does not exist within the * namespace, and Unionfs operates on the namespace, not data. */ if (sbgen != dgen) { struct dentry *result; int pdgen; unionfs_read_lock(dentry->d_sb); locked = 1; /* The root entry should always be valid */ BUG_ON(IS_ROOT(dentry)); /* We can't work correctly if our parent isn't valid. */ pdgen = atomic_read(&dtopd(dentry->d_parent)->udi_generation); if (!restart && (pdgen != sbgen)) { unionfs_read_unlock(dentry->d_sb); locked = 0; /* We must be locked before our parent. */ if (! (dentry->d_parent->d_op-> d_revalidate(dentry->d_parent, nd))) { valid = 0; goto out; } restart = 1; goto restart; } BUG_ON(pdgen != sbgen); /* Free the pointers for our inodes and this dentry. */ bstart = dbstart(dentry); bend = dbend(dentry); if (bstart >= 0) { struct dentry *hidden_dentry; for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index_nocheck(dentry, bindex); if (!hidden_dentry) continue; DPUT(hidden_dentry); } } set_dbstart(dentry, -1); set_dbend(dentry, -1); interpose_flag = INTERPOSE_REVAL_NEG; if (positive) { interpose_flag = INTERPOSE_REVAL; down(&dentry->d_inode->i_sem); bstart = ibstart(dentry->d_inode); bend = ibend(dentry->d_inode); if (bstart >= 0) { struct inode *hidden_inode; for (bindex = bstart; bindex <= bend; bindex++) { hidden_inode = itohi_index(dentry->d_inode, bindex); if (!hidden_inode) continue; IPUT(hidden_inode); } } KFREE(itohi_ptr(dentry->d_inode)); itohi_ptr(dentry->d_inode) = NULL; ibstart(dentry->d_inode) = -1; ibend(dentry->d_inode) = -1; up(&dentry->d_inode->i_sem); } result = unionfs_lookup_backend(dentry, interpose_flag); if (result) { if (IS_ERR(result)) { valid = 0; goto out; } /* current unionfs_lookup_backend() doesn't return a valid dentry */ DPUT(dentry); dentry = result; } if (positive && itopd(dentry->d_inode)->uii_stale) { make_stale_inode(dentry->d_inode); d_drop(dentry); valid = 0; goto out; } goto out; } /* The revalidation must occur across all branches */ bstart = dbstart(dentry); bend = dbend(dentry); BUG_ON(bstart == -1); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry || !hidden_dentry->d_op || !hidden_dentry->d_op->d_revalidate) continue; if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd)) valid = 0; } if (!dentry->d_inode) valid = 0; if (valid) fist_copy_attr_all(dentry->d_inode, itohi(dentry->d_inode)); out: if (locked) unionfs_read_unlock(dentry->d_sb); fist_print_dentry("revalidate out", dentry); print_util_exit_status(valid); return valid; }
static int do_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, int bindex, struct dentry **wh_old) { int err = 0; struct dentry *hidden_old_dentry; struct dentry *hidden_new_dentry; struct dentry *hidden_old_dir_dentry; struct dentry *hidden_new_dir_dentry; struct dentry *hidden_wh_dentry; struct dentry *hidden_wh_dir_dentry; char *wh_name = NULL; print_entry(" bindex=%d", bindex); fist_print_dentry("IN: do_rename, old_dentry", old_dentry); fist_print_dentry("IN: do_rename, new_dentry", new_dentry); fist_dprint(7, "do_rename for bindex = %d\n", bindex); hidden_new_dentry = dtohd_index(new_dentry, bindex); hidden_old_dentry = dtohd_index(old_dentry, bindex); if (!hidden_new_dentry) { hidden_new_dentry = create_parents(new_dentry->d_parent->d_inode, new_dentry, bindex); if (IS_ERR(hidden_new_dentry)) { fist_dprint(7, "error creating directory tree for rename, bindex = %d\n", bindex); err = PTR_ERR(hidden_new_dentry); goto out; } } wh_name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len); if (IS_ERR(wh_name)) { err = PTR_ERR(wh_name); goto out; } hidden_wh_dentry = LOOKUP_ONE_LEN(wh_name, hidden_new_dentry->d_parent, new_dentry->d_name.len + WHLEN); if (IS_ERR(hidden_wh_dentry)) { err = PTR_ERR(hidden_wh_dentry); goto out; } if (hidden_wh_dentry->d_inode) { /* get rid of the whiteout that is existing */ if (hidden_new_dentry->d_inode) { printk(KERN_WARNING "Both a whiteout and a dentry exist when doing a rename!\n"); err = -EIO; DPUT(hidden_wh_dentry); goto out; } hidden_wh_dir_dentry = lock_parent(hidden_wh_dentry); if (!(err = is_robranch_super(old_dentry->d_sb, bindex))) { err = vfs_unlink(hidden_wh_dir_dentry->d_inode, hidden_wh_dentry); } DPUT(hidden_wh_dentry); unlock_dir(hidden_wh_dir_dentry); if (err) goto out; } else DPUT(hidden_wh_dentry); DGET(hidden_old_dentry); hidden_old_dir_dentry = GET_PARENT(hidden_old_dentry); hidden_new_dir_dentry = GET_PARENT(hidden_new_dentry); lock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry); err = is_robranch_super(old_dentry->d_sb, bindex); if (err) goto out_unlock; /* ready to whiteout for old_dentry. caller will create the actual whiteout, and must dput(*wh_old) */ if (wh_old) { char *whname; whname = alloc_whname(old_dentry->d_name.name, old_dentry->d_name.len); err = PTR_ERR(whname); if (IS_ERR(whname)) goto out_unlock; *wh_old = LOOKUP_ONE_LEN(whname, hidden_old_dir_dentry, old_dentry->d_name.len + WHLEN); KFREE(whname); err = PTR_ERR(*wh_old); if (IS_ERR(*wh_old)) { *wh_old = NULL; goto out_unlock; } } fist_print_dentry("NEWBEF", new_dentry); fist_print_dentry("OLDBEF", old_dentry); err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, hidden_new_dir_dentry->d_inode, hidden_new_dentry); fist_print_dentry("NEWAFT", new_dentry); fist_print_dentry("OLDAFT", old_dentry); out_unlock: unlock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry); DPUT(hidden_old_dir_dentry); DPUT(hidden_new_dir_dentry); DPUT(hidden_old_dentry); out: if (!err) { /* Fixup the newdentry. */ if (bindex < dbstart(new_dentry)) set_dbstart(new_dentry, bindex); else if (bindex > dbend(new_dentry)) set_dbend(new_dentry, bindex); } KFREE(wh_name); fist_print_dentry("OUT: do_rename, old_dentry", old_dentry); fist_print_dentry("OUT: do_rename, new_dentry", new_dentry); print_exit_status(err); return err; }
/* This function replicates the directory structure upto given dentry * in the bindex branch. */ struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry, const char *name, int bindex) { int err; struct dentry *child_dentry; struct dentry *parent_dentry; struct dentry *hidden_parent_dentry = NULL; struct dentry *hidden_dentry = NULL; const char *childname; unsigned int childnamelen; int old_kmalloc_size; int kmalloc_size; int num_dentry; int count; int old_bstart; int old_bend; struct dentry **path = NULL; struct dentry **tmp_path; print_entry_location(); verify_locked(dentry); /* There is no sense allocating any less than the minimum. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) kmalloc_size = malloc_sizes[0].cs_size; #else kmalloc_size = 32; #endif num_dentry = kmalloc_size / sizeof(struct dentry *); if ((err = is_robranch_super(dir->i_sb, bindex))) { hidden_dentry = ERR_PTR(err); goto out; } fist_print_dentry("IN: create_parents_named", dentry); fist_dprint(8, "name = %s\n", name); old_bstart = dbstart(dentry); old_bend = dbend(dentry); path = (struct dentry **)KMALLOC(kmalloc_size, GFP_KERNEL); memset(path, 0, kmalloc_size); /* assume the negative dentry of unionfs as the parent dentry */ parent_dentry = dentry; count = 0; /* This loop finds the first parent that exists in the given branch. * We start building the directory structure from there. At the end * of the loop, the following should hold: * child_dentry is the first nonexistent child * parent_dentry is the first existent parent * path[0] is the = deepest child * path[count] is the first child to create */ do { child_dentry = parent_dentry; /* find the parent directory dentry in unionfs */ parent_dentry = child_dentry->d_parent; lock_dentry(parent_dentry); /* find out the hidden_parent_dentry in the given branch */ hidden_parent_dentry = dtohd_index(parent_dentry, bindex); /* store the child dentry */ path[count++] = child_dentry; if (count == num_dentry) { old_kmalloc_size = kmalloc_size; kmalloc_size *= 2; num_dentry = kmalloc_size / sizeof(struct dentry *); tmp_path = (struct dentry **)KMALLOC(kmalloc_size, GFP_KERNEL); if (!tmp_path) { hidden_dentry = ERR_PTR(-ENOMEM); goto out; } memset(tmp_path, 0, kmalloc_size); memcpy(tmp_path, path, old_kmalloc_size); KFREE(path); path = tmp_path; tmp_path = NULL; } } while (!hidden_parent_dentry); count--; /* This is basically while(child_dentry != dentry). This loop is * horrible to follow and should be replaced with cleaner code. */ while (1) { PASSERT(child_dentry); PASSERT(parent_dentry); PASSERT(parent_dentry->d_inode); // get hidden parent dir in the current branch hidden_parent_dentry = dtohd_index(parent_dentry, bindex); unlock_dentry(parent_dentry); PASSERT(hidden_parent_dentry); PASSERT(hidden_parent_dentry->d_inode); // init the values to lookup childname = child_dentry->d_name.name; childnamelen = child_dentry->d_name.len; if (child_dentry != dentry) { // lookup child in the underlying file system hidden_dentry = LOOKUP_ONE_LEN(childname, hidden_parent_dentry, childnamelen); if (IS_ERR(hidden_dentry)) goto out; } else { int loop_start; int loop_end; int new_bstart = -1; int new_bend = -1; int i; /* is the name a whiteout of the childname ? */ //lookup the whiteout child in the underlying file system hidden_dentry = LOOKUP_ONE_LEN(name, hidden_parent_dentry, strlen(name)); if (IS_ERR(hidden_dentry)) goto out; /* Replace the current dentry (if any) with the new one. */ DPUT(dtohd_index(dentry, bindex)); set_dtohd_index(dentry, bindex, hidden_dentry); loop_start = (old_bstart < bindex) ? old_bstart : bindex; loop_end = (old_bend > bindex) ? old_bend : bindex; /* This loop sets the bstart and bend for the new * dentry by traversing from left to right. * It also dputs all negative dentries except * bindex (the newly looked dentry */ for (i = loop_start; i <= loop_end; i++) { if (!dtohd_index(dentry, i)) continue; if (i == bindex) { new_bend = i; if (new_bstart < 0) new_bstart = i; continue; } if (!dtohd_index(dentry, i)->d_inode) { DPUT(dtohd_index(dentry, i)); set_dtohd_index(dentry, i, NULL); } else { if (new_bstart < 0) new_bstart = i; new_bend = i; } } if (new_bstart < 0) new_bstart = bindex; if (new_bend < 0) new_bend = bindex; set_dbstart(dentry, new_bstart); set_dbend(dentry, new_bend); break; } if (hidden_dentry->d_inode) { /* since this already exists we dput to avoid * multiple references on the same dentry */ DPUT(hidden_dentry); } else { uid_t saved_uid = current->fsuid; gid_t saved_gid = current->fsgid; /* its a negative dentry, create a new dir */ hidden_parent_dentry = lock_parent(hidden_dentry); current->fsuid = hidden_parent_dentry->d_inode->i_uid; current->fsgid = hidden_parent_dentry->d_inode->i_gid; err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, S_IRWXUGO); current->fsuid = saved_uid; current->fsgid = saved_gid; unlock_dir(hidden_parent_dentry); if (err || !hidden_dentry->d_inode) { DPUT(hidden_dentry); hidden_dentry = ERR_PTR(err); goto out; } err = copyup_permissions(dir->i_sb, child_dentry, hidden_dentry); if (err) { DPUT(hidden_dentry); hidden_dentry = ERR_PTR(err); goto out; } set_itohi_index(child_dentry->d_inode, bindex, igrab(hidden_dentry->d_inode)); if (ibstart(child_dentry->d_inode) > bindex) ibstart(child_dentry->d_inode) = bindex; if (ibend(child_dentry->d_inode) < bindex) ibend(child_dentry->d_inode) = bindex; set_dtohd_index(child_dentry, bindex, hidden_dentry); if (dbstart(child_dentry) > bindex) set_dbstart(child_dentry, bindex); if (dbend(child_dentry) < bindex) set_dbend(child_dentry, bindex); } parent_dentry = child_dentry; child_dentry = path[--count]; } out: KFREE(path); fist_print_dentry("OUT: create_parents_named", dentry); print_exit_pointer(hidden_dentry); return hidden_dentry; }
static int unionfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *whiteout_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; umode_t mode; int bindex = 0, bstart; char *name = NULL; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_symlink", dentry); /* We start out in the leftmost branch. */ bstart = dbstart(dentry); hidden_dentry = dtohd(dentry); /* check if whiteout exists in this branch, i.e. lookup .wh.foo first. If present, delete it */ name = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, dentry->d_name.len + WHLEN); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { /* found a .wh.foo entry, unlink it and then call vfs_symlink() */ hidden_dir_dentry = lock_parent(whiteout_dentry); fist_print_generic_dentry("HDD", hidden_dir_dentry); fist_print_generic_dentry("WD", whiteout_dentry); if (!(err = is_robranch_super(dentry->d_sb, bstart))) { err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry); } DPUT(whiteout_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* propagate number of hard-links */ dir->i_nlink = get_nlinks(dir); unlock_dir(hidden_dir_dentry); if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; /* should now try to create symlink in the another branch */ bstart--; } } /* deleted whiteout if it was present, now do a normal vfs_symlink() with possible recursive directory creation */ for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { /* if hidden_dentry is NULL, create the entire * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a * lone negative underlying dentry */ hidden_dentry = create_parents(dir, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { if (IS_ERR(hidden_dentry)) { err = PTR_ERR(hidden_dentry); } fist_dprint(8, "hidden dentry NULL (or error) for bindex = %d\n", bindex); continue; } } hidden_dir_dentry = lock_parent(hidden_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { mode = S_IALLUGO; err = vfs_symlink(hidden_dir_dentry->d_inode, hidden_dentry, symname, mode); } unlock_dir(hidden_dir_dentry); if (err || !hidden_dentry->d_inode) { /* break out of for loop if error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { err = unionfs_interpose(dentry, dir->i_sb, 0); if (!err) { fist_copy_attr_timesizes(dir, hidden_dir_dentry-> d_inode); /* update number of links on parent directory */ dir->i_nlink = get_nlinks(dir); } break; } } out: if (!dentry->d_inode) d_drop(dentry); KFREE(name); fist_print_dentry("OUT unionfs_symlink :", dentry); unlock_dentry(dentry); print_exit_status(err); return err; }