/* * Copyright (c) 2003-2009 Erez Zadok * Copyright (c) 2003-2006 Charles P. Wright * Copyright (c) 2005-2007 Josef 'Jeff' Sipek * Copyright (c) 2005 Arun M. Krishnakumar * Copyright (c) 2004-2006 David P. Quigley * Copyright (c) 2003-2004 Mohammad Nayyer Zubair * Copyright (c) 2003 Puja Gupta * Copyright (c) 2003 Harikesavan Krishnan * Copyright (c) 2003-2009 Stony Brook University * Copyright (c) 2003-2009 The Research Foundation of SUNY * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef _UNION_H_ #define _UNION_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* the file system name */ #define UNIONFS_NAME "unionfs" /* unionfs root inode number */ #define UNIONFS_ROOT_INO 1 /* number of times we try to get a unique temporary file name */ #define GET_TMPNAM_MAX_RETRY 5 /* maximum number of branches we support, to avoid memory blowup */ #define UNIONFS_MAX_BRANCHES 128 /* minimum time (seconds) required for time-based cache-coherency */ #define UNIONFS_MIN_CC_TIME 3 /* Operations vectors defined in specific files. */ extern struct file_operations unionfs_main_fops; extern struct file_operations unionfs_dir_fops; extern struct inode_operations unionfs_main_iops; extern struct inode_operations unionfs_dir_iops; extern struct inode_operations unionfs_symlink_iops; extern struct super_operations unionfs_sops; extern struct dentry_operations unionfs_dops; extern struct address_space_operations unionfs_aops, unionfs_dummy_aops; extern struct vm_operations_struct unionfs_vm_ops; /* How long should an entry be allowed to persist */ #define RDCACHE_JIFFIES (5*HZ) /* compatibility with Real-Time patches */ #ifdef CONFIG_PREEMPT_RT # define unionfs_rw_semaphore compat_rw_semaphore #else /* not CONFIG_PREEMPT_RT */ # define unionfs_rw_semaphore rw_semaphore #endif /* not CONFIG_PREEMPT_RT */ /* file private data. */ struct unionfs_file_info { int bstart; int bend; atomic_t generation; struct unionfs_dir_state *rdstate; struct file **lower_files; int *saved_branch_ids; /* IDs of branches when file was opened */ struct vm_operations_struct *lower_vm_ops; bool wrote_to_file; /* for delayed copyup */ }; /* unionfs inode data in memory */ struct unionfs_inode_info { int bstart; int bend; atomic_t generation; /* Stuff for readdir over NFS. */ spinlock_t rdlock; struct list_head readdircache; int rdcount; int hashsize; int cookie; /* The lower inodes */ struct inode **lower_inodes; struct inode vfs_inode; }; /* unionfs dentry data in memory */ struct unionfs_dentry_info { /* * The semaphore is used to lock the dentry as soon as we get into a * unionfs function from the VFS. Our lock ordering is that children * go before their parents. */ struct mutex lock; int bstart; int bend; int bopaque; int bcount; atomic_t generation; struct path *lower_paths; }; /* These are the pointers to our various objects. */ struct unionfs_data { struct super_block *sb; /* lower super_block */ atomic_t open_files; /* number of open files on branch */ int branchperms; int branch_id; /* unique branch ID at re/mount time */ }; /* unionfs super-block data in memory */ struct unionfs_sb_info { int bend; atomic_t generation; /* * This rwsem is used to make sure that a branch management * operation... * 1) will not begin before all currently in-flight operations * complete. * 2) any new operations do not execute until the currently * running branch management operation completes. * * The write_lock_owner records the PID of the task which grabbed * the rw_sem for writing. If the same task also tries to grab the * read lock, we allow it. This prevents a self-deadlock when * branch-management is used on a pivot_root'ed union, because we * have to ->lookup paths which belong to the same union. */ struct unionfs_rw_semaphore rwsem; pid_t write_lock_owner; /* PID of rw_sem owner (write lock) */ int high_branch_id; /* last unique branch ID given */ char *dev_name; /* to identify different unions in pr_debug */ struct unionfs_data *data; }; /* * structure for making the linked list of entries by readdir on left branch * to compare with entries on right branch */ struct filldir_node { struct list_head file_list; /* list for directory entries */ char *name; /* name entry */ int hash; /* name hash */ int namelen; /* name len since name is not 0 terminated */ /* * we can check for duplicate whiteouts and files in the same branch * in order to return -EIO. */ int bindex; /* is this a whiteout entry? */ int whiteout; /* Inline name, so we don't need to separately kmalloc small ones */ char iname[DNAME_INLINE_LEN_MIN]; }; /* Directory hash table. */ struct unionfs_dir_state { unsigned int cookie; /* the cookie, based off of rdversion */ unsigned int offset; /* The entry we have returned. */ int bindex; loff_t dirpos; /* offset within the lower level directory */ int size; /* How big is the hash table? */ int hashentries; /* How many entries have been inserted? */ unsigned long access; /* This cache list is used when the inode keeps us around. */ struct list_head cache; struct list_head list[0]; }; /* externs needed for fanout.h or sioq.h */ extern int unionfs_get_nlinks(const struct inode *inode); extern void unionfs_copy_attr_times(struct inode *upper); extern void unionfs_copy_attr_all(struct inode *dest, const struct inode *src); /* include miscellaneous macros */ #include "fanout.h" #include "sioq.h" /* externs for cache creation/deletion routines */ extern void unionfs_destroy_filldir_cache(void); extern int unionfs_init_filldir_cache(void); extern int unionfs_init_inode_cache(void); extern void unionfs_destroy_inode_cache(void); extern int unionfs_init_dentry_cache(void); extern void unionfs_destroy_dentry_cache(void); /* Initialize and free readdir-specific state. */ extern int init_rdstate(struct file *file); extern struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex); extern struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos); extern void free_rdstate(struct unionfs_dir_state *state); extern int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen, int bindex, int whiteout); extern struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen, int is_whiteout); extern struct dentry **alloc_new_dentries(int objs); extern struct unionfs_data *alloc_new_data(int objs); /* We can only use 32-bits of offset for rdstate --- blech! */ #define DIREOF (0xfffff) #define RDOFFBITS 20 /* This is the number of bits in DIREOF. */ #define MAXRDCOOKIE (0xfff) /* Turn an rdstate into an offset. */ static inline off_t rdstate2offset(struct unionfs_dir_state *buf) { off_t tmp; tmp = ((buf->cookie & MAXRDCOOKIE) << RDOFFBITS) | (buf->offset & DIREOF); return tmp; } /* Macros for locking a super_block. */ enum unionfs_super_lock_class { UNIONFS_SMUTEX_NORMAL, UNIONFS_SMUTEX_PARENT, /* when locking on behalf of file */ UNIONFS_SMUTEX_CHILD, /* when locking on behalf of dentry */ }; static inline void unionfs_read_lock(struct super_block *sb, int subclass) { if (UNIONFS_SB(sb)->write_lock_owner && UNIONFS_SB(sb)->write_lock_owner == current->pid) return; down_read_nested(&UNIONFS_SB(sb)->rwsem, subclass); } static inline void unionfs_read_unlock(struct super_block *sb) { if (UNIONFS_SB(sb)->write_lock_owner && UNIONFS_SB(sb)->write_lock_owner == current->pid) return; up_read(&UNIONFS_SB(sb)->rwsem); } static inline void unionfs_write_lock(struct super_block *sb) { down_write(&UNIONFS_SB(sb)->rwsem); UNIONFS_SB(sb)->write_lock_owner = current->pid; } static inline void unionfs_write_unlock(struct super_block *sb) { up_write(&UNIONFS_SB(sb)->rwsem); UNIONFS_SB(sb)->write_lock_owner = 0; } static inline void unionfs_double_lock_dentry(struct dentry *d1, struct dentry *d2) { BUG_ON(d1 == d2); if (d1 < d2) { unionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD); } else { unionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD); } } static inline void unionfs_double_unlock_dentry(struct dentry *d1, struct dentry *d2) { BUG_ON(d1 == d2); if (d1 < d2) { /* unlock in reverse order than double_lock_dentry */ unionfs_unlock_dentry(d1); unionfs_unlock_dentry(d2); } else { unionfs_unlock_dentry(d2); unionfs_unlock_dentry(d1); } } static inline void unionfs_double_lock_parents(struct dentry *p1, struct dentry *p2) { if (p1 == p2) { unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT); return; } if (p1 < p2) { unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT); unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_CHILD); } else { unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_PARENT); unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_CHILD); } } static inline void unionfs_double_unlock_parents(struct dentry *p1, struct dentry *p2) { if (p1 == p2) { unionfs_unlock_dentry(p1); return; } if (p1 < p2) { /* unlock in reverse order of double_lock_parents */ unionfs_unlock_dentry(p1); unionfs_unlock_dentry(p2); } else { unionfs_unlock_dentry(p2); unionfs_unlock_dentry(p1); } } extern int new_dentry_private_data(struct dentry *dentry, int subclass); extern int realloc_dentry_private_data(struct dentry *dentry); extern void free_dentry_private_data(struct dentry *dentry); extern void update_bstart(struct dentry *dentry); extern int init_lower_nd(struct nameidata *nd, unsigned int flags); extern void release_lower_nd(struct nameidata *nd, int err); /* * EXTERNALS: */ /* replicates the directory structure up to given dentry in given branch */ extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry, const char *name, int bindex); /* partial lookup */ extern int unionfs_partial_lookup(struct dentry *dentry, struct dentry *parent); extern struct dentry *unionfs_lookup_full(struct dentry *dentry, struct dentry *parent, int lookupmode); /* copies a file from dbstart to newbindex branch */ extern int copyup_file(struct inode *dir, struct file *file, int bstart, int newbindex, loff_t size); extern int copyup_named_file(struct inode *dir, struct file *file, char *name, int bstart, int new_bindex, loff_t len); /* copies a dentry from dbstart to newbindex branch */ extern int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, const char *name, int namelen, struct file **copyup_file, loff_t len); /* helper functions for post-copyup actions */ extern void unionfs_postcopyup_setmnt(struct dentry *dentry); extern void unionfs_postcopyup_release(struct dentry *dentry); /* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */ extern int check_empty(struct dentry *dentry, struct dentry *parent, struct unionfs_dir_state **namelist); /* whiteout and opaque directory helpers */ extern char *alloc_whname(const char *name, int len); extern bool is_whiteout_name(char **namep, int *namelenp); extern bool is_validname(const char *name); extern struct dentry *lookup_whiteout(const char *name, struct dentry *lower_parent); extern struct dentry *find_first_whiteout(struct dentry *dentry); extern int unlink_whiteout(struct dentry *wh_dentry); extern int check_unlink_whiteout(struct dentry *dentry, struct dentry *lower_dentry, int bindex); extern int create_whiteout(struct dentry *dentry, int start); extern int delete_whiteouts(struct dentry *dentry, int bindex, struct unionfs_dir_state *namelist); extern int is_opaque_dir(struct dentry *dentry, int bindex); extern int make_dir_opaque(struct dentry *dir, int bindex); extern void unionfs_set_max_namelen(long *namelen); extern void unionfs_reinterpose(struct dentry *this_dentry); extern struct super_block *unionfs_duplicate_super(struct super_block *sb); /* Locking functions. */ extern int unionfs_setlk(struct file *file, int cmd, struct file_lock *fl); extern int unionfs_getlk(struct file *file, struct file_lock *fl); /* Common file operations. */ extern int unionfs_file_revalidate(struct file *file, struct dentry *parent, bool willwrite); extern int unionfs_open(struct inode *inode, struct file *file); extern int unionfs_file_release(struct inode *inode, struct file *file); extern int unionfs_flush(struct file *file, fl_owner_t id); extern long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); extern int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync); extern int unionfs_fasync(int fd, struct file *file, int flag); /* Inode operations */ extern struct inode *unionfs_iget(struct super_block *sb, unsigned long ino); extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); extern int unionfs_unlink(struct inode *dir, struct dentry *dentry); extern int unionfs_rmdir(struct inode *dir, struct dentry *dentry); extern bool __unionfs_d_revalidate(struct dentry *dentry, struct dentry *parent, bool willwrite); extern bool is_negative_lower(const struct dentry *dentry); extern bool is_newer_lower(const struct dentry *dentry); extern void purge_sb_data(struct super_block *sb); /* The values for unionfs_interpose's flag. */ #define INTERPOSE_DEFAULT 0 #define INTERPOSE_LOOKUP 1 #define INTERPOSE_REVAL 2 #define INTERPOSE_REVAL_NEG 3 #define INTERPOSE_PARTIAL 4 extern struct dentry *unionfs_interpose(struct dentry *this_dentry, struct super_block *sb, int flag); #ifdef CONFIG_UNION_FS_XATTR /* Extended attribute functions. */ extern void *unionfs_xattr_alloc(size_t size, size_t limit); static inline void unionfs_xattr_kfree(const void *p) { kfree(p); } extern ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size); extern int unionfs_removexattr(struct dentry *dentry, const char *name); extern ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size); extern int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); #endif /* CONFIG_UNION_FS_XATTR */ /* The root directory is unhashed, but isn't deleted. */ static inline int d_deleted(struct dentry *d) { return d_unhashed(d) && (d != d->d_sb->s_root); } /* unionfs_permission, check if we should bypass error to facilitate copyup */ #define IS_COPYUP_ERR(err) ((err) == -EROFS) /* unionfs_open, check if we need to copyup the file */ #define OPEN_WRITE_FLAGS (O_WRONLY | O_RDWR | O_APPEND) #define IS_WRITE_FLAG(flag) ((flag) & OPEN_WRITE_FLAGS) static inline int branchperms(const struct super_block *sb, int index) { BUG_ON(index < 0); return UNIONFS_SB(sb)->data[index].branchperms; } static inline int set_branchperms(struct super_block *sb, int index, int perms) { BUG_ON(index < 0); UNIONFS_SB(sb)->data[index].branchperms = perms; return perms; } /* Is this file on a read-only branch? */ static inline int is_robranch_super(const struct super_block *sb, int index) { int ret; ret = (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0; return ret; } /* Is this file on a read-only branch? */ static inline int is_robranch_idx(const struct dentry *dentry, int index) { struct super_block *lower_sb; BUG_ON(index < 0); if (!(branchperms(dentry->d_sb, index) & MAY_WRITE)) return -EROFS; lower_sb = unionfs_lower_super_idx(dentry->d_sb, index); BUG_ON(lower_sb == NULL); /* * test sb flags directly, not IS_RDONLY(lower_inode) because the * lower_dentry could be a negative. */ if (lower_sb->s_flags & MS_RDONLY) return -EROFS; return 0; } static inline int is_robranch(const struct dentry *dentry) { int index; index = UNIONFS_D(dentry)->bstart; BUG_ON(index < 0); return is_robranch_idx(dentry, index); } /* * EXTERNALS: */ extern int check_branch(struct nameidata *nd); extern int parse_branch_mode(const char *name, int *perms); /* locking helpers */ static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); return dir; } static inline struct dentry *lock_parent_wh(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); mutex_lock_nested(&dir->d_inode->i_mutex, UNIONFS_DMUTEX_WHITEOUT); return dir; } static inline void unlock_dir(struct dentry *dir) { mutex_unlock(&dir->d_inode->i_mutex); dput(dir); } static inline struct vfsmount *unionfs_mntget(struct dentry *dentry, int bindex) { struct vfsmount *mnt; BUG_ON(!dentry || bindex < 0); mnt = mntget(unionfs_lower_mnt_idx(dentry, bindex)); #ifdef CONFIG_UNION_FS_DEBUG if (!mnt) pr_debug("unionfs: mntget: mnt=%p bindex=%d\n", mnt, bindex); #endif /* CONFIG_UNION_FS_DEBUG */ return mnt; } static inline void unionfs_mntput(struct dentry *dentry, int bindex) { struct vfsmount *mnt; if (!dentry && bindex < 0) return; BUG_ON(!dentry || bindex < 0); mnt = unionfs_lower_mnt_idx(dentry, bindex); #ifdef CONFIG_UNION_FS_DEBUG /* * Directories can have NULL lower objects in between start/end, but * NOT if at the start/end range. We cannot verify that this dentry * is a type=DIR, because it may already be a negative dentry. But * if dbstart is greater than dbend, we know that this couldn't have * been a regular file: it had to have been a directory. */ if (!mnt && !(bindex > dbstart(dentry) && bindex < dbend(dentry))) pr_debug("unionfs: mntput: mnt=%p bindex=%d\n", mnt, bindex); #endif /* CONFIG_UNION_FS_DEBUG */ mntput(mnt); } #ifdef CONFIG_UNION_FS_DEBUG /* useful for tracking code reachability */ #define UDBG pr_debug("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) #define unionfs_check_inode(i) __unionfs_check_inode((i), \ __FILE__, __func__, __LINE__) #define unionfs_check_dentry(d) __unionfs_check_dentry((d), \ __FILE__, __func__, __LINE__) #define unionfs_check_file(f) __unionfs_check_file((f), \ __FILE__, __func__, __LINE__) #define unionfs_check_nd(n) __unionfs_check_nd((n), \ __FILE__, __func__, __LINE__) #define show_branch_counts(sb) __show_branch_counts((sb), \ __FILE__, __func__, __LINE__) #define show_inode_times(i) __show_inode_times((i), \ __FILE__, __func__, __LINE__) #define show_dinode_times(d) __show_dinode_times((d), \ __FILE__, __func__, __LINE__) #define show_inode_counts(i) __show_inode_counts((i), \ __FILE__, __func__, __LINE__) extern void __unionfs_check_inode(const struct inode *inode, const char *fname, const char *fxn, int line); extern void __unionfs_check_dentry(const struct dentry *dentry, const char *fname, const char *fxn, int line); extern void __unionfs_check_file(const struct file *file, const char *fname, const char *fxn, int line); extern void __unionfs_check_nd(const struct nameidata *nd, const char *fname, const char *fxn, int line); extern void __show_branch_counts(const struct super_block *sb, const char *file, const char *fxn, int line); extern void __show_inode_times(const struct inode *inode, const char *file, const char *fxn, int line); extern void __show_dinode_times(const struct dentry *dentry, const char *file, const char *fxn, int line); extern void __show_inode_counts(const struct inode *inode, const char *file, const char *fxn, int line); #else /* not CONFIG_UNION_FS_DEBUG */ /* we leave useful hooks for these check functions throughout the code */ #define unionfs_check_inode(i) do { } while (0) #define unionfs_check_dentry(d) do { } while (0) #define unionfs_check_file(f) do { } while (0) #define unionfs_check_nd(n) do { } while (0) #define show_branch_counts(sb) do { } while (0) #define show_inode_times(i) do { } while (0) #define show_dinode_times(d) do { } while (0) #define show_inode_counts(i) do { } while (0) #endif /* not CONFIG_UNION_FS_DEBUG */ #endif /* not _UNION_H_ */