/* * Called by iput() when the inode reference count reached zero * and the inode is not hashed anywhere. Used to clear anything * that needs to be, before the inode is completely destroyed and put * on the inode free list. */ STATIC void mini_fo_clear_inode(inode_t *inode) { /* * Decrement a reference to a hidden_inode, which was incremented * by our read_inode when it was created initially. */ /* release the wol_list */ if(S_ISDIR(inode->i_mode)) { __meta_put_lists(inode); } /* mk: fan out fun */ if(itohi(inode)) iput(itohi(inode)); if(itohi2(inode)) iput(itohi2(inode)); // XXX: why this assertion fails? // because it doesn't like us // ASSERT((inode->i_state & I_DIRTY) == 0); kfree(itopd(inode)); __itopd(inode) = NULL; }
STATIC void mini_fo_read_inode(inode_t *inode) { static struct address_space_operations mini_fo_empty_aops; __itopd(inode) = kmalloc(sizeof(struct mini_fo_inode_info), GFP_KERNEL); if (!itopd(inode)) { printk("<0>%s:%s:%d: No kernel memory!\n", __FILE__, __FUNCTION__, __LINE__); ASSERT(NULL); } itohi(inode) = NULL; itohi2(inode) = NULL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) inode->i_version++; #else inode->i_version = ++event; /* increment inode version */ #endif inode->i_op = &mini_fo_main_iops; inode->i_fop = &mini_fo_main_fops; #if 0 /* * XXX: To export a file system via NFS, it has to have the * FS_REQUIRES_DEV flag, so turn it on. But should we inherit it from * the lower file system, or can we allow our file system to be exported * even if the lower one cannot be natively exported. */ inode->i_sb->s_type->fs_flags |= FS_REQUIRES_DEV; /* * OK, the above was a hack, which is now turned off because it may * cause a panic/oops on some systems. The correct way to export a * "nodev" filesystem is via using nfs-utils > 1.0 and the "fsid=" export * parameter, which requires 2.4.20 or later. */ #endif /* I don't think ->a_ops is ever allowed to be NULL */ inode->i_mapping->a_ops = &mini_fo_empty_aops; }
/* * 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; }
/* 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 int unionfs_setattr(struct dentry *dentry, struct iattr *ia) { int err = 0; struct dentry *hidden_dentry; struct inode *inode = NULL; struct inode *hidden_inode = NULL; int bstart, bend, bindex; int i; int copyup = 0; print_entry_location(); lock_dentry(dentry); bstart = dbstart(dentry); bend = dbend(dentry); inode = dentry->d_inode; for (bindex = bstart; (bindex <= bend) || (bindex == bstart); bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; BUG_ON(hidden_dentry->d_inode == NULL); /* If the file is on a read only branch */ if (is_robranch_super(dentry->d_sb, bindex) || IS_RDONLY(hidden_dentry->d_inode)) { if (copyup || (bindex != bstart)) continue; /* Only if its the leftmost file, copyup the file */ for (i = bstart - 1; i >= 0; i--) { size_t size = dentry->d_inode->i_size; if (ia->ia_valid & ATTR_SIZE) size = ia->ia_size; err = copyup_dentry(dentry->d_parent->d_inode, dentry, bstart, i, NULL, size); if (!err) { copyup = 1; hidden_dentry = dtohd(dentry); break; } /* if error is in the leftmost f/s, pass it up */ if (i == 0) goto out; } } err = notify_change(hidden_dentry, ia); if (err) goto out; break; } /* get the size from the first hidden inode */ hidden_inode = itohi(dentry->d_inode); fist_checkinode(inode, "unionfs_setattr"); fist_copy_attr_all(inode, hidden_inode); out: unlock_dentry(dentry); fist_checkinode(inode, "post unionfs_setattr"); print_exit_status(err); return err; }