Logo Search packages:      
Sourcecode: unionfs version File versions

unionfs.h

/*
 * Copyright (c) 2003-2005 Erez Zadok
 * Copyright (c) 2003-2005 Charles P. Wright
 * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
 * Copyright (c) 2003-2005 Puja Gupta
 * Copyright (c) 2003-2005 Harikesavan Krishnan
 * Copyright (c) 2003-2005 Stony Brook University
 * Copyright (c) 2003-2005 The Research Foundation of State University of New York
 *
 * For specific licensing information, see the COPYING file distributed with
 * this package.
 *
 * This Copyright notice must be kept intact and distributed with all sources.
 */
/*
 *  $Id: unionfs.h,v 1.104 2005/03/24 03:26:46 dquigley Exp $
 */

#ifndef __UNIONFS_H_
#define __UNIONFS_H_

#ifdef __KERNEL__

#if (!defined(UNIONFS_UNSUPPORTED)) &&(LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9))
#warning You are compiling Unionfs on an unsupported kernel version.
#warning To compile Unionfs, you will need to define UNIONFS_UNSUPPORTED.
#warning Try adding: EXTRACFLAGS=-DUNIONFS_UNSUPPORTED to fistdev.mk
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#include <linux/spinlock.h>
#define wq_write_lock_irqsave(lock,flags) spin_lock_irqsave(lock,flags)
#define wq_write_unlock(lock) spin_unlock(lock)
#define wq_write_lock_irq(lock) spin_lock_irq(lock)
#define wq_write_unlock_irqrestore(lock,flags) spin_unlock_irqrestore(lock,flags)
#endif

/* fist file systems superblock magic */
#define UNIONFS_SUPER_MAGIC 0xf15f

/* unionfs root inode number */
#define UNIONFS_ROOT_INO     1

/* Mount time flags */
#define MOUNT_FLAG(sb)     (stopd(sb)->usi_mount_flag)

/* maximum number of parent directories to create. */
/* XXX: This shouldn't be so small, and it should be kmalloced. */
#define MAX_DIR_CREATE  10

/* What type of flags to use for kmalloc, etc. */
#define GFP_UNIONFS GFP_KERNEL

/* A lock structure that a single task can lock N times and then unlock N times. */
struct multilock {
      spinlock_t ml_protect;
      struct task_struct *ml_locker;
      int ml_depth;
      wait_queue_head_t ml_sleepers;
};

static inline void multilock_init(struct multilock *ml) {
#ifdef MULTILOCK_TRACK
      printk("MLI:%p\n", ml);
#endif
      ml->ml_protect = SPIN_LOCK_UNLOCKED;
      ml->ml_locker = NULL;
      ml->ml_depth = 0;
      init_waitqueue_head(&ml->ml_sleepers);
}

#define multilock_lock(ml) __multilock_lock((ml), __FILE__, __FUNCTION__, __LINE__)
static inline void __multilock_lock(struct multilock *ml, const char *file, const char *function, int line) {

#ifdef MULTILOCK_TRACK
      printk("ILK:%d:%p:%d (%s:%d)\n", current->pid, ml, ml->ml_depth, function, line);
#endif

retry:
      spin_lock(&ml->ml_protect);
      if (ml->ml_locker == NULL) {
            ml->ml_locker = current;
            ml->ml_depth = 1;
      }
      else if (ml->ml_locker == current) {
            ml->ml_depth++;
      } else {
            /* SLEEP_ON_VAR */
            wait_queue_t wait;
            unsigned long flags;
            init_waitqueue_entry(&wait, current);

            /* We want to go to sleep */
            current->state = TASK_UNINTERRUPTIBLE;

            /* SLEEP_ON_HEAD */
            wq_write_lock_irqsave(&ml->ml_sleepers.lock, flags);
            __add_wait_queue(&ml->ml_sleepers, &wait);
            wq_write_unlock(&ml->ml_sleepers.lock);

            /* This line is why we can't use sleep_on, because we need to save our queue. */
            spin_unlock(&ml->ml_protect);

            schedule();

            /* SLEEP_ON_TAIL */
            wq_write_lock_irq(&ml->ml_sleepers.lock);
            __remove_wait_queue(&ml->ml_sleepers, &wait);
            wq_write_unlock_irqrestore(&ml->ml_sleepers.lock, flags);
            goto retry;
      }
      spin_unlock(&ml->ml_protect);

#ifdef MULTILOCK_TRACK
      printk("OLK:%d:%p:%d\n", current->pid, ml, ml->ml_depth);
#endif
}

#define multilock_unlock(ml) __multilock_unlock((ml), __FILE__, __FUNCTION__, __LINE__)
static inline void __multilock_unlock(struct multilock *ml, const char *file, const char *function, int line) {
#ifdef MULTILOCK_TRACK
      printk("IUL:%d:%p:%d (%s:%d)\n", current->pid, ml, ml->ml_depth, function, line);
#endif
      spin_lock(&ml->ml_protect);
      ASSERT2(ml->ml_depth > 0);
      ASSERT2(ml->ml_locker == current);
      ml->ml_depth--;
      if (ml->ml_depth == 0) {
            ml->ml_locker = NULL;
            wake_up(&ml->ml_sleepers);
      }
      spin_unlock(&ml->ml_protect);
#ifdef MULTILOCK_TRACK
      printk("OUL:%d:%p:%d\n", current->pid, ml, ml->ml_depth);
#endif
}

/* 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 export_operations unionfs_export_ops;

/* How long should an entry be allowed to persist */
#define RDCACHE_JIFFIES 5*HZ
/* unionfs inode data in memory */
struct unionfs_inode_info {
    inode_t ** uii_inode;
    int b_start;
    int b_end;
    atomic_t uii_generation;
    int uii_stale;
    /* Stuff for readdir over NFS. */
    spinlock_t uii_rdlock;
    struct list_head uii_readdircache;
    int uii_rdversion;
    int uii_rdcount;
    int uii_hashsize;
      int uii_cookie;
};

/* unionfs dentry data in memory */
struct unionfs_dentry_info {
    struct multilock udi_lock;
    dentry_t ** udi_dentry;
    int udi_bstart;
    int udi_bend;
    int udi_bcount;
    atomic_t udi_generation;
};

/* unionfs super-block data in memory */
struct unionfs_sb_info {
    struct super_block *usi_thissb;
    super_block_t **usi_sb;
    atomic_t *usi_sbcount;
    vfs_mount_t **usi_hidden_mnt;
    int *usi_branchperms;

    int b_start;
    int b_end;

    uid_t copyupuid;
    gid_t copyupgid;
    umode_t copyupmode;

    atomic_t usi_generation;
    unsigned long usi_mount_flag;

#ifdef SPLIT_VIEW_CACHES
    struct super_block *usi_primary;
    struct list_head usi_altsupers;
#endif
};

/*
 * structure for making the linked list of entrues 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
    int bindex;                   // we can check for duplicate whiteouts and files in the same branch in order to return -EIO.
    int whiteout;         // is this a whiteout entry?
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#define DNAME_INLINE_LEN_MIN DNAME_INLINE_LEN
#endif
    char iname[DNAME_INLINE_LEN_MIN]; // Inline name, so we don't need to separately kmalloc small ones
};

/* Filldir cache creation/deletion. */
void destroy_filldir_cache(void);
int init_filldir_cache(void);

/* Initialize and free readdir-specific  state. */
int init_rdstate(struct file *file);
struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex);
struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos);
void free_rdstate(struct unionfs_dir_state *state);
int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen, int bindex, int whiteout);
struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen);

/* Directory hash table. */
struct unionfs_dir_state {
    unsigned int uds_cookie; /* The cookie, which is based off of uii_rdversion */
      unsigned int uds_offset; /* The entry we have returned. */
    int uds_bindex;
      loff_t uds_dirpos; /* The offset within the lower level directory. */
    int uds_size; /* How big is the hash table? */
    int uds_hashentries; /* How many entries have been inserted? */
    unsigned long uds_access;
    /* This cache list is used when the inode keeps us around. */
    struct list_head uds_cache;
    struct list_head uds_list[0];
};
/* 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->uds_cookie & MAXRDCOOKIE) << RDOFFBITS) | (buf->uds_offset & DIREOF);
      return tmp;
}
/* Make sure our rdstate is playing by the rules. */
static inline void verify_rdstate_offset(struct unionfs_dir_state *rdstate) {
      PASSERT(rdstate);
      ASSERT(rdstate->uds_offset < DIREOF);
      ASSERT(rdstate->uds_cookie < MAXRDCOOKIE);
}


/* file private data. */
struct unionfs_file_info {
    struct file ** ufi_file;
    int b_start;
    int b_end;
    atomic_t ufi_generation;
    struct unionfs_dir_state *rdstate;
};

/*
 * MACROS:
 */
// File TO Private Data
#define ftopd(file) ((struct unionfs_file_info *)((file)->private_data))
#define ftopd_lhs(file) ((file)->private_data)
// File TO Hidden File
#define ftohf(file) (ftopd(file)->ufi_file[ftopd(file)->b_start])
// File to Hidden File based on index
#define ftohf_index(file, index) ((ftopd(file))->ufi_file[index])
// File to Hidden File Pointers
#define ftohf_ptr(file)  (ftopd(file)->ufi_file)
// Macros to refer to branch indices
#define fbstart(file) (ftopd(file)->b_start)
#define fbend(file) (ftopd(file)->b_end)
// Initialize private data for inode
#define init_ftopd(file) \
     {\
        memset(ftopd(file), 0, sizeof(struct unionfs_file_info));\
        fbstart(file) = -1; \
        fbend(file) = -1;\
     }
/* Initialize array pointers for hidden files */
#define init_ftohf_ptr(file, num) memset(ftohf_ptr(file), 0, (sizeof(file_t *) * num))

// Inode TO Private Data
#define itopd(ino) ((struct unionfs_inode_info *)(ino)->u.generic_ip)
#define itopd_lhs(ino) ((ino)->u.generic_ip)
// Inode TO Hidden Inode
// #define itohi(ino) itopd(ino)->uii_inode[itopd(ino)->b_start]
#define itohi(ino) __itohi(ino, __FILE__, __FUNCTION__, __LINE__)
static inline struct inode *__itohi(struct inode *ino, const char *file, const char *function, int line) {
    struct inode *hidden_inode;
    ASSERT2(itopd(ino)->b_start >= 0);
    hidden_inode = itopd(ino)->uii_inode[itopd(ino)->b_start];
    ASSERT2((atomic_read(&hidden_inode->i_count)) > 0);
    return hidden_inode;
}

// Inode To Hidden Inode based on index
#define itohi_index(ino, index) (itopd(ino)->uii_inode[index])
// Inode To Hidden Inode Pointers
#define itohi_ptr(ino)  (itopd(ino)->uii_inode)
#define vnode2lower itohi
// Macros to refer to branch indices
#define ibstart(ino) (itopd(ino)->b_start)
#define ibend(ino) (itopd(ino)->b_end)
// Initialize private data for inode
#define init_itopd(ino)  \
    {\
        PASSERT(ino->i_sb);\
        memset(itopd(ino), 0, sizeof(struct unionfs_inode_info));\
        itopd(ino)->b_start = -1; \
        itopd(ino)->b_end = -1;\
        atomic_set(&itopd(ino)->uii_generation, atomic_read(&stopd(ino->i_sb)->usi_generation));\
      itopd(ino)->uii_rdlock = SPIN_LOCK_UNLOCKED;\
      itopd(ino)->uii_rdcount = 1;\
      itopd(ino)->uii_hashsize = -1;\
      INIT_LIST_HEAD(&itopd(ino)->uii_readdircache);\
    }
// Initialize array pointers for inode
#define init_itohi_ptr(inode, num) memset(itohi_ptr(inode), 0, (sizeof(inode_t *) * num))

// Superblock TO Private Data
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#define stopd(super) ((struct unionfs_sb_info *)(super)->u.generic_sbp)
#define stopd_lhs(super) ((super)->u.generic_sbp)
#else
#define stopd(super) ((struct unionfs_sb_info *)(super)->s_fs_info)
#define stopd_lhs(super) ((super)->s_fs_info)
#endif

// Superblock TO Hidden Superblock
#define stohs(super) (stopd(super)->usi_sb[stopd(super)->b_start])
// Superblock to Hidden Superblock based on branch index
#define stohs_index(super, index) (stopd(super)->usi_sb[index])
// Superblock to Hidden Array of super blocks
#define stohs_ptr(super) (stopd(super)->usi_sb)
// Superblock to Hidden Mount point
#define stohiddenmnt_index(super, index) (stopd(super)->usi_hidden_mnt[index])
// Superblock to Hidden Mount Pointer
#define stohiddenmnt_ptr(super) (stopd(super)->usi_hidden_mnt)
// Macros to refer to the branch indices
#define sbstart(sb) stopd(sb)->b_start
#define sbend(sb) stopd(sb)->b_end
#define sbmax(sb) (stopd(sb)->b_end + 1)
// Initialize private data for super block

#define init_stopd(sb) \
    {\
        memset(stopd(sb), 0, sizeof(struct unionfs_sb_info)); \
      stopd(sb)->usi_thissb = sb; \
        stopd(sb)->b_start = -1;\
        stopd(sb)->b_end = -1;\
      atomic_set(&stopd(sb)->usi_generation, 1);\
      init_alt_super(sb); \
    }

#ifdef SPLIT_VIEW_CACHES
#define init_alt_super(sb) \
    {\
      stopd(sb)->usi_primary = sb; \
      INIT_LIST_HEAD(&stopd(sb)->usi_altsupers); \
    }
#else
#define init_alt_super(sb)
#endif

// Initialize array pointers for hidden superblock
#define init_stohs_ptr(sb, num) memset(stohs_ptr(sb), 0, (sizeof(super_block_t *) * num))
#define init_stohiddenmnt_ptr(sb, num) memset(stohiddenmnt_ptr(sb), 0, (sizeof(vfs_mount_t *) * num))

/* Get and put branches on the superblock. */
#define branchget(sb, index) atomic_inc(&stopd(sb)->usi_sbcount[index]);
#define branchput(sb, index) atomic_dec(&stopd(sb)->usi_sbcount[index]);

#define lock_dpd(dentry) multilock_lock(&dtopd(dentry)->udi_lock)
#define unlock_dpd(dentry) multilock_unlock(&dtopd(dentry)->udi_lock)

#define lock_dpd2(dentry) __multilock_lock(&dtopd(dentry)->udi_lock, file, function, line)
#define unlock_dpd2(dentry) __multilock_unlock(&dtopd(dentry)->udi_lock, file, function, line)

// Dentry to Hidden Dentry Pointer
#define dtohd_ptr(dentry) (dtopd(dentry)->udi_dentry)
// Dentry TO Hidden Dentry (and private data --- for the future)
#define dtopd(dent) __dtopd(dent, 1, __FILE__, __FUNCTION__, __LINE__)
static inline struct unionfs_dentry_info *__dtopd(const struct dentry *dent, int check, const char *file, const char *function, int line) {
      struct unionfs_dentry_info *ret;

      PASSERT2(dent);
      ret = (struct unionfs_dentry_info *)(dent)->d_fsdata;
      /* We are really only interested in catching poison here. */
      if (ret) {
            PASSERT2(ret);
            if (check) {
                  if ((ret->udi_bend > ret->udi_bcount) || (ret->udi_bend > sbmax(dent->d_sb))) {
                        printk("udi_bend = %d, udi_count = %d, sbmax = %d\n", ret->udi_bend, ret->udi_bcount, sbmax(dent->d_sb));
                  }
                  ASSERT2(ret->udi_bend <= ret->udi_bcount);
                  ASSERT2(ret->udi_bend <= sbmax(dent->d_sb));
            }
      }

      return ret;
}

#define dtopd_nocheck(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata)
#define dtopd_lhs(dent) ((dent)->d_fsdata)

// Dentry to Hidden dentry
// #define dtohd(dent) dtopd(dent)->udi_dentry[dtopd(dent)->b_start]
#define dtohd(dent) __dtohd(dent, __FILE__, __FUNCTION__, __LINE__)
static inline struct dentry *__dtohd(struct dentry *dent, const char *file, const char *function, int line) {
    struct dentry *d;

    lock_dpd2(dent);

    PASSERT2(dent);
    PASSERT2(dent->d_op);
    ASSERT2(dent->d_op == &unionfs_dops);
    ASSERT2(dent->d_sb->s_op == &unionfs_sops);
    if (dent->d_inode) {
      PASSERT2(dent->d_inode);
      ASSERT(dent->d_inode->i_op == &unionfs_main_iops ||
            dent->d_inode->i_op == &unionfs_dir_iops ||
            dent->d_inode->i_op == &unionfs_symlink_iops);
    }

    ASSERT2(dtopd(dent)->udi_bstart >= 0);
    d = dtopd(dent)->udi_dentry[dtopd(dent)->udi_bstart];
    if (d) {
      PASSERT2(d);
      ASSERT2((atomic_read(&d->d_count)) > 0);
    }

    unlock_dpd2(dent);

    return d;
}

// Dentry to Hidden Dentry based on index
#define set_dtohd_index(dent, index, val) __set_dtohd_index(dent, index, val, __FILE__, __FUNCTION__, __LINE__)
static inline struct dentry *__set_dtohd_index(struct dentry *dent, int index, struct dentry *val, const char *file, const char *function, int line) {
    PASSERT2(dent);

    lock_dpd2(dent);

    PASSERT2(dent->d_sb);
    PASSERT2(dtopd(dent));
    PASSERT2(dtopd(dent)->udi_dentry);
    ASSERT2(index >= 0);
    if (index > sbend(dent->d_sb)) {
      printk("Dentry index out of super bounds: index=%d, sbend=%d\n", index, sbend(dent->d_sb));
        ASSERT2(index <= sbend(dent->d_sb));
    }
    if (index > dtopd(dent)->udi_bcount) {
      printk("Dentry index out of array bounds: index=%d, count=%d\n", index, dtopd(dent)->udi_bcount);
      printk("Generation of dentry: %d\n", atomic_read(&dtopd(dent)->udi_generation));
      printk("Generation of sb: %d\n", atomic_read(&stopd(dent->d_sb)->usi_generation));
        ASSERT2(index <= dtopd(dent)->udi_bcount);
    }
    if (val) {
      PASSERT2(val);
    }
    dtopd(dent)->udi_dentry[index] = val;

    unlock_dpd2(dent);
    return val;
}

#define dtohd_index(dent, index) __dtohd_index(dent, index, __FILE__, __FUNCTION__, __LINE__)
static inline struct dentry *__dtohd_index(const struct dentry *dent, int index, const char *file, const char *function, int line) {
    struct dentry *d;

    PASSERT2(dent);
    lock_dpd2(dent);
    PASSERT2(dent->d_sb);
    PASSERT2(dtopd(dent));
    PASSERT2(dtopd(dent)->udi_dentry);
    ASSERT2(index >= 0);
    if (index > sbend(dent->d_sb)) {
      printk("Dentry index out of super bounds: index=%d, sbend=%d\n", index, sbend(dent->d_sb));
        ASSERT2(index <= sbend(dent->d_sb));
    }
    if (index > dtopd(dent)->udi_bcount) {
      printk("Dentry index out of array bounds: index=%d, count=%d\n", index, dtopd(dent)->udi_bcount);
      printk("Generation of dentry: %d\n", atomic_read(&dtopd(dent)->udi_generation));
      printk("Generation of sb: %d\n", atomic_read(&stopd(dent->d_sb)->usi_generation));
        ASSERT2(index <= dtopd(dent)->udi_bcount);
    }
    d = dtopd(dent)->udi_dentry[index];
    if (d) {
      PASSERT2(d);
      ASSERT2((atomic_read(&d->d_count)) > 0);
    }
    unlock_dpd2(dent);
    return d;
}
// Macros to refer to branch indices

#define dbend(dentry) __dbend(dentry, __FILE__, __FUNCTION__, __LINE__)
static inline int __dbend(const struct dentry *dentry, const char *file, const char *function, int line) {
    return dtopd(dentry)->udi_bend;
}
#define set_dbend(dentry, val) __set_dbend(dentry, val, __FILE__, __FUNCTION__, __LINE__)
static inline int __set_dbend(const struct dentry *dentry, int val, const char *file, const char *function, int line) {
    if (val < 0) {
      ASSERT2(val == -1);
    }
    ASSERT2(val <= __dtopd(dentry, 0, file, function, line)->udi_bcount);
    ASSERT2(val <= sbmax(dentry->d_sb));
    dtopd(dentry)->udi_bend = val;
    return __dtopd(dentry, 1, file, function, line)->udi_bend;
}


#define dbstart(dentry) __dbstart(dentry, __FILE__, __FUNCTION__, __LINE__)
static inline int __dbstart(const struct dentry *dentry, const char *file, const char *function, int line) {
    return dtopd(dentry)->udi_bstart;
}
#define set_dbstart(dentry, val) __set_dbstart(dentry, val, __FILE__, __FUNCTION__, __LINE__)
static inline int __set_dbstart(const struct dentry *dentry, int val, const char *file, const char *function, int line) {
    if (val < 0) {
      ASSERT2(val == -1);
    }
    ASSERT2(val <= __dtopd(dentry, 0, file, function, line)->udi_bcount);
    ASSERT2(val <= sbmax(dentry->d_sb));
    __dtopd(dentry, 0, file, function, line)->udi_bstart = val;
    return __dtopd(dentry, 1, file, function, line)->udi_bstart;
}


#define init_dtopd(dentry, sb) \
    {\
      multilock_init(&dtopd_nocheck(dentry)->udi_lock); \
      reinit_dtopd(dentry, sb); \
    }

#define reinit_dtopd(dentry, sb) {\
      dtopd_nocheck(dentry)->udi_dentry = NULL;\
        dtopd_nocheck(dentry)->udi_bstart = -1;\
        dtopd_nocheck(dentry)->udi_bend = -1;\
        dtopd_nocheck(dentry)->udi_bcount = 0;\
        atomic_set(&dtopd_nocheck(dentry)->udi_generation, atomic_read(&stopd(sb)->usi_generation));\
    }
#define init_dentry_info(ptr) { \
        memset(ptr, 0, sizeof(struct unionfs_dentry_info));\
        ptr->udi_bstart = -1;\
        ptr->udi_bend = -1;\
}
// Initialize array pointers for sb
#define init_dtohd_ptr(dent, num) do { \
      struct dentry *d = dent; \
      dtopd(d)->udi_bcount = num; \
      memset(dtohd_ptr(d), 0, (sizeof(dentry_t *) * num)); \
} while(0)

#define sbt(sb) ((sb)->s_type->name)

/*
 * EXTERNALS:
 */
/* replicates the directory structure upto given dentry in given branch */
extern dentry_t *unionfs_create_dirs(inode_t *dir, dentry_t *dentry, int bindex);

/* partial lookup */
extern int unionfs_partial_lookup(dentry_t *dentry);

/* Pass an unionfs dentry and an index and it will try to create a whiteout in branch 'index'.
   On error, it will proceed to a branch to the left */
extern int create_whiteout(dentry_t *dentry, int start);
extern int create_whiteout_parent(dentry_t *parent_dentry, const char *filename, int start);
/* copies a file from dbstart to newbindex branch */
extern int unionfs_copyup_file(inode_t *dir, file_t *file, int bstart, int newbindex, int size);

/* copies a dentry from dbstart to newbindex branch */
extern int unionfs_copyup_dentry_len(inode_t *dir, dentry_t *dentry, int bstart, int new_bindex, file_t **copyup_file, int len);

extern int create_dir_whs(dentry_t *dentry, int cur_index) ;

extern int remove_whiteouts(dentry_t *dentry, dentry_t *hidden_dentry, int bindex);

/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */
extern int check_empty(dentry_t *dentry, struct unionfs_dir_state **namelist);
/* Delete whiteouts from this directory in branch bindex. */
extern int delete_whiteouts(struct dentry *dentry, int bindex, struct unionfs_dir_state *namelist);

/* Re-lookup a hidden dentry. */
extern int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex);

extern void unionfs_reinterpose(dentry_t *this_dentry);
extern struct super_block *unionfs_duplicate_super(super_block_t *sb);

/* Locking functions. */
extern int unionfs_setlk(file_t *file, int cmd, struct file_lock *fl);
extern int unionfs_getlk(file_t *file, struct file_lock *fl);

/* 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_LINK        4

extern int unionfs_interpose(dentry_t *this_dentry, super_block_t *sb, int flag);

/* Branch management ioctls. */
int unionfs_ioctl_branchcount(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
int unionfs_ioctl_incgen(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
int unionfs_ioctl_addbranch(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
int unionfs_ioctl_delbranch(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
int unionfs_ioctl_rdwrbranch(inode_t *inode, file_t *unused_file, unsigned int cmd, unsigned long arg);
int unionfs_ioctl_superduper(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);


#if defined(UNIONFS_XATTR) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
/* Extended attribute functions. */
extern void *xattr_alloc(size_t size, size_t limit);
extern void xattr_free(void *ptr, size_t size);

extern int unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size);
extern int unionfs_removexattr(struct dentry *dentry, const char *name);
extern int unionfs_listxattr(struct dentry *dentry, char *list, size_t size);

#if defined(FIST_SETXATTR_CONSTVOID) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
#else
int unionfs_setxattr(struct dentry *dentry, const char *name, void *value, size_t size, int flags);
#endif

#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */

#define copy_inode_size(dst, src) \
    dst->i_size = src->i_size; \
    dst->i_blocks = src->i_blocks;

/* returns the sum of the n_link values of all the underlying inodes of the passed inode */
static inline int
get_nlinks(inode_t *inode)
{
    int sum_nlinks = 2, bindex;
    inode_t *hidden_inode;

    PASSERT(inode);
    if (!S_ISDIR(inode->i_mode)) {
      return itohi(inode)->i_nlink;
    }

    for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
        hidden_inode = itohi_index(inode, bindex);
      if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode)) {
          continue;
      }
      sum_nlinks += (hidden_inode->i_nlink - 2);
    }
    return sum_nlinks;
}

static inline void
fist_copy_attr_atime(inode_t *dest, const inode_t *src)
{
    PASSERT(dest);
    PASSERT(src);
    dest->i_atime = src->i_atime;
}
static inline void
fist_copy_attr_times(inode_t *dest, const inode_t *src)
{
    PASSERT(dest);
    PASSERT(src);
    dest->i_atime = src->i_atime;
    dest->i_mtime = src->i_mtime;
    dest->i_ctime = src->i_ctime;
}
static inline void
fist_copy_attr_timesizes(inode_t *dest, const inode_t *src)
{
    PASSERT(dest);
    PASSERT(src);
    dest->i_atime = src->i_atime;
    dest->i_mtime = src->i_mtime;
    dest->i_ctime = src->i_ctime;
    copy_inode_size(dest, src);
}
static inline void
fist_copy_attr_all(inode_t *dest, const inode_t *src)
{

    print_entry_location();
    PASSERT(dest);
    PASSERT(src);
    dest->i_mode = src->i_mode;
    dest->i_nlink = get_nlinks(dest);
    dest->i_uid = src->i_uid;
    dest->i_gid = src->i_gid;
    dest->i_rdev = src->i_rdev;
    dest->i_atime = src->i_atime;
    dest->i_mtime = src->i_mtime;
    dest->i_ctime = src->i_ctime;
    dest->i_blksize = src->i_blksize;
    dest->i_blkbits = src->i_blkbits;
    copy_inode_size(dest, src);

//DQ: This was a change I noticed in the templates. In 2.6 they removedi_attr_flags.
//Which makes me think they rolled it into flags.
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
    dest->i_attr_flags = src->i_attr_flags;
#else
    dest->i_flags = src->i_flags;
#endif

    print_exit_location();
}


dentry_t *unionfs_lookup_backend(inode_t *dir, dentry_t *dentry, int interpose_flag);
int is_stale_inode(struct inode * inode);
void make_stale_inode(struct inode * inode);

#define IS_SET(sb, check_flag) (check_flag & MOUNT_FLAG(sb))

/* 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 IS_WRITE_FLAG(flag) (flag & (O_WRONLY | O_RDWR | O_APPEND))

/* Is this file on a read-only branch? */
static inline int __is_robranch_super(struct super_block *sb, int index, char *file, const char *function, int line) {
    int err = 0;

    print_util_entry_location();

    ASSERT(index >= 0);
    PASSERT(sb);
    PASSERT(stopd(sb));
    PASSERT(stopd(sb)->usi_branchperms);

    if (!(stopd(sb)->usi_branchperms[index] & MAY_WRITE)) {
      err = -EROFS;
    }
    print_util_exit_status(err);
    return err;
}

/* Is this file on a read-only branch? */
static inline int __is_robranch_index(struct dentry *dentry, int index, char *file, const char *function, int line) {
    int err = 0;

    print_util_entry_location();

    PASSERT2(dentry);
    ASSERT2(index >= 0);
    PASSERT2(dtohd_index(dentry, index));
    PASSERT2(dtohd_index(dentry, index)->d_inode);

    if (!(stopd(dentry->d_sb)->usi_branchperms[index] & MAY_WRITE)) {
      err = -EROFS;
    } else if (IS_RDONLY(dtohd_index(dentry, index)->d_inode)) {
      err = -EROFS;
    }

    print_util_exit_status(err);

    return err;
}
static inline int __is_robranch(struct dentry *dentry, char *file, const char *function, int line) {
    int index;
    int err;

    print_util_entry_location();

    PASSERT2(dentry);
    PASSERT2(dtopd(dentry));
    index = dtopd(dentry)->udi_bstart;
    ASSERT2(index >= 0);

    err =  __is_robranch_index(dentry, index, file, function, line);

    print_util_exit_status(err);

    return err;
}

#define is_robranch(d) __is_robranch(d, __FILE__, __FUNCTION__, __LINE__)
#define is_robranch_super(s, n) __is_robranch_super(s, n, __FILE__, __FUNCTION__, __LINE__)

/* auto-generated extern definitions */

#endif /* __KERNEL__ */

/*
 * Definitions for user and kernel code
 */

/* Definitions for various ways to handle errors.
   Each flag's value is its bit position */

/* 1 = on error, passup,  0 = try to recover */
#define GLOBAL_ERR_PASSUP     0

/* 1 = delete first file/directory, 0 = delete all */
#define DELETE_FIRST          2

/* 1 = delete whiteout, 0 = check for DELETE_FIRST */
#define DELETE_WHITEOUT       4

/* 1 = use current user's permissions, 0 = use original owner's permissions */
#define COPYUP_CURRENT_USER     8

/* 1 = f/s mounter permission, 0 = check for COPYUP_OWNER */
#define COPYUP_FS_MOUNTER     16

/* 1 = set attributes for all underlying files, 0 = leftmost file */
#define SETATTR_ALL             32

#define VALID_MOUNT_FLAGS (GLOBAL_ERR_PASSUP | DELETE_FIRST | DELETE_WHITEOUT | COPYUP_OWNER | COPYUP_FS_MOUNTER | SETATTR_ALL)

/* ioctls */

#define EUNIONFS_NO_WHITEOUT   1025
#endif      /* not __UNIONFS_H_ */

/*
 * Local variables:
 * c-basic-offset: 4
 * End:
 */

Generated by  Doxygen 1.6.0   Back to index