diff -rupN --exclude='ide*' linux-2.4.20.orig/include/linux/mm.h linux-2.4.20/include/linux/mm.h --- linux-2.4.20.orig/include/linux/mm.h 2004-02-10 11:43:10.000000000 +0200 +++ linux-2.4.20/include/linux/mm.h 2004-03-01 13:44:45.000000000 +0200 @@ -468,7 +468,8 @@ extern void clear_page_tables(struct mm_ extern int fail_writepage(struct page *); struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused); struct file *shmem_file_setup(char * name, loff_t size); -int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr); +struct page *shmem_getpage_locked(struct inode *inode, unsigned long idx); +struct page *shmem_getpage_unlocked(struct inode *inode, unsigned long idx); extern void shmem_lock(struct file * file, int lock); extern int shmem_zero_setup(struct vm_area_struct *); diff -rupN --exclude='ide*' linux-2.4.20.orig/include/linux/shmem_fs.h linux-2.4.20/include/linux/shmem_fs.h --- linux-2.4.20.orig/include/linux/shmem_fs.h 2004-02-10 18:39:17.000000000 +0200 +++ linux-2.4.20/include/linux/shmem_fs.h 2004-02-23 12:40:28.000000000 +0200 @@ -7,6 +7,9 @@ #define SHMEM_NR_DIRECT 16 +#define SHMEM_MOUNT_IOPEN 0x8000 /* Allow access via iopen */ +#define SHMEM_MOUNT_IOPEN_NOPRIV 0x10000 /* Make iopen world-readable */ + /* * A swap entry has to fit into a "unsigned long", as * the entry is hidden in the "index" field of the @@ -38,6 +41,9 @@ struct shmem_inode_info { }; struct shmem_sb_info { + struct dentry *iopen; + unsigned long options; + unsigned long root_ino; unsigned long max_blocks; /* How many blocks are allowed */ unsigned long free_blocks; /* How many are left for allocation */ unsigned long max_inodes; /* How many inodes are allowed */ @@ -59,11 +65,9 @@ shmem_xattr_find(struct inode *inode, co extern ssize_t shmem_xattr_set(struct inode *inode, const char *name, const void *value, u16 valuelen, int flags); - extern ssize_t shmem_xattr_get(struct inode *inode, const char *name, void *value, size_t valuelen); - extern int shmem_xattr_delete(struct inode *inode, struct shmem_xattr *xattr); diff -rupN --exclude='ide*' linux-2.4.20.orig/mm/shmem.c linux-2.4.20/mm/shmem.c --- linux-2.4.20.orig/mm/shmem.c 2004-02-10 18:44:05.000000000 +0200 +++ linux-2.4.20/mm/shmem.c 2004-03-01 14:37:21.000000000 +0200 @@ -36,29 +36,45 @@ #define TMPFS_MAGIC 0x01021994 #define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long)) + #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) -#define SHMEM_MAX_INDEX (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * (ENTRIES_PER_PAGE/2) * (ENTRIES_PER_PAGE+1)) +#define SHMEM_MAX_INDEX (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * \ + (ENTRIES_PER_PAGE/2) * (ENTRIES_PER_PAGE+1)) + #define SHMEM_MAX_BYTES ((unsigned long long)SHMEM_MAX_INDEX << PAGE_CACHE_SHIFT) #define VM_ACCT(size) (((size) + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT) /* Pretend that each entry is of this size in directory's i_size */ -#define BOGO_DIRENT_SIZE 20 - +#define BOGO_DIRENT_SIZE (20) #define SHMEM_SB(sb) (&sb->u.shmem_sb) +#define SHMEM_IOPEN_INO 2 +#define SHMEM_IOPEN_NAME_LEN 32 + +#define ASSERT(cond) \ +do { \ + if (!(cond)) { \ + printk (KERN_EMERG \ + "Assertion failure in %s() at %s:%d: \"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # cond); \ + BUG(); \ + } \ +} while (0) + static struct super_operations shmem_ops; +static struct vm_operations_struct shmem_vm_ops; static struct address_space_operations shmem_aops; static struct file_operations shmem_file_operations; static struct inode_operations shmem_inode_operations; static struct inode_operations shmem_dir_inode_operations; -static struct vm_operations_struct shmem_vm_ops; LIST_HEAD (shmem_inodes); static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED; atomic_t shmem_nrpages = ATOMIC_INIT(0); /* Not used right now */ -static struct page *shmem_getpage_locked(struct shmem_inode_info *, struct inode *, unsigned long); +struct page *shmem_getpage_locked(struct inode *inode, unsigned long idx); +struct page *shmem_getpage_unlocked(struct inode *inode, unsigned long idx); #ifdef CONFIG_TMPFS static struct inode_operations shmem_symlink_inode_operations; @@ -327,7 +343,7 @@ shmem_getxattr(struct dentry *dentry, co * inode attributes list.*/ static int shmem_setxattr(struct dentry *dentry, const char *name, - void *value, size_t valuelen, int flags) + const void *value, size_t valuelen, int flags) { int error; struct inode *inode = dentry->d_inode; @@ -404,8 +420,8 @@ shmem_listxattr(struct dentry *dentry, c * @inode: inode to recalc * @swap: additional swap pages freed externally * - * We have to calculate the free blocks since the mm can drop pages - * behind our back + * We have to calculate the free blocks since the mm can drop pages behind our + * back * * But we know that normally * inodes->i_blocks/BLOCKS_PER_PAGE == @@ -441,24 +457,23 @@ static void shmem_recalc_inode(struct in * @page: optional page to add to the structure. Has to be preset to * all zeros * - * If there is no space allocated yet it will return -ENOMEM when - * page == 0 else it will use the page for the needed block. + * If there is no space allocated yet it will return -ENOMEM when page == 0 else + * it will use the page for the needed block. * * returns -EFBIG if the index is too big. * * * The swap vector is organized the following way: * - * There are SHMEM_NR_DIRECT entries directly stored in the - * shmem_inode_info structure. So small files do not need an addional - * allocation. - * - * For pages with index > SHMEM_NR_DIRECT there is the pointer - * i_indirect which points to a page which holds in the first half - * doubly indirect blocks, in the second half triple indirect blocks: + * There are SHMEM_NR_DIRECT entries directly stored in the shmem_inode_info + * structure. So small files do not need an addional allocation. + * + * For pages with index > SHMEM_NR_DIRECT there is the pointer i_indirect which + * points to a page which holds in the first half doubly indirect blocks, in the + * second half triple indirect blocks: * - * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the - * following layout (for SHMEM_NR_DIRECT == 16): + * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the following + * layout (for SHMEM_NR_DIRECT == 16): * * i_indirect -> dir --> 16-19 * | +-> 20-23 @@ -473,7 +488,9 @@ static void shmem_recalc_inode(struct in * +-> 48-51 * +-> 52-55 */ -static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index, unsigned long page) +static swp_entry_t * +shmem_swp_entry (struct shmem_inode_info *info, unsigned long index, + unsigned long page) { unsigned long offset; void **dir; @@ -520,7 +537,8 @@ static swp_entry_t * shmem_swp_entry (st * @info: info structure for the inode * @index: index of the page to find */ -static inline swp_entry_t * shmem_alloc_entry (struct shmem_inode_info *info, unsigned long index) +static inline swp_entry_t * +shmem_alloc_entry(struct shmem_inode_info *info, unsigned long index) { unsigned long page = 0; swp_entry_t * res; @@ -545,7 +563,8 @@ static inline swp_entry_t * shmem_alloc_ * @dir: pointer to the directory * @count: number of entries to scan */ -static int shmem_free_swp(swp_entry_t *dir, unsigned int count) +static int +shmem_free_swp(swp_entry_t *dir, unsigned int count) { swp_entry_t *ptr, entry; int freed = 0; @@ -573,7 +592,9 @@ static int shmem_free_swp(swp_entry_t *d */ static inline unsigned long -shmem_truncate_direct(swp_entry_t *** dir, unsigned long start, unsigned long len) { +shmem_truncate_direct(swp_entry_t ***dir, unsigned long start, + unsigned long len) +{ swp_entry_t **last, **ptr; unsigned long off, freed = 0; @@ -639,7 +660,8 @@ shmem_truncate_indirect(struct shmem_ino BUG(); baseidx = max & ~(ENTRIES_PER_PAGE*ENTRIES_PER_PAGE-1); - base = (swp_entry_t ***) info->i_indirect + ENTRIES_PER_PAGE/2 + baseidx/ENTRIES_PER_PAGE/ENTRIES_PER_PAGE ; + base = (swp_entry_t ***) info->i_indirect + ENTRIES_PER_PAGE/2 + + baseidx/ENTRIES_PER_PAGE/ENTRIES_PER_PAGE ; len = max - baseidx + 1; baseidx += ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2+SHMEM_NR_DIRECT; } @@ -654,7 +676,8 @@ shmem_truncate_indirect(struct shmem_ino return shmem_truncate_direct(base, start, len); } -static void shmem_truncate (struct inode * inode) +static void +shmem_truncate(struct inode *inode) { unsigned long index; unsigned long partial; @@ -668,16 +691,16 @@ static void shmem_truncate (struct inode partial = inode->i_size & ~PAGE_CACHE_MASK; if (partial) { - swp_entry_t *entry = shmem_swp_entry(info, index-1, 0); struct page *page; - /* - * This check is racy: it's faintly possible that page - * was assigned to swap during truncate_inode_pages, - * and now assigned to file; but better than nothing. + swp_entry_t *entry = shmem_swp_entry(info, index - 1, 0); + + /* This check is racy: it's faintly possible that page was + * assigned to swap during truncate_inode_pages, and now + * assigned to file; but better than nothing. */ if (!IS_ERR(entry) && entry->val) { spin_unlock(&info->lock); - page = shmem_getpage_locked(info, inode, index-1); + page = shmem_getpage_locked(inode, index - 1); if (!IS_ERR(page)) { memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE - partial); @@ -697,8 +720,166 @@ static void shmem_truncate (struct inode up(&info->sem); } -static void shmem_delete_inode(struct inode * inode) +static struct inode * +shmem_find_inode(struct super_block *sb, long int ino) +{ + struct list_head *p; + struct inode *inode = NULL; + struct shmem_inode_info *info; + + spin_lock (&shmem_ilock); + list_for_each(p, &shmem_inodes) { + info = list_entry(p, struct shmem_inode_info, list); + + if (info->inode->i_ino == ino && + info->inode->i_sb == sb) + { + inode = info->inode; + break; + } + } + + spin_unlock (&shmem_ilock); + + if (inode) + igrab(inode); + + return inode; +} + +#define switch_fields(x,y) do { \ + __typeof__ (x) __tmp = x; \ + x = y; y = __tmp; } while (0) + +static inline void +switch_names(struct dentry *dentry, struct dentry *target) +{ + const unsigned char *old_name, *new_name; + + memcpy(dentry->d_iname, target->d_iname, + DNAME_INLINE_LEN); + + old_name = target->d_name.name; + new_name = dentry->d_name.name; + + if (old_name == target->d_iname) + old_name = dentry->d_iname; + + if (new_name == dentry->d_iname) + new_name = target->d_iname; + + target->d_name.name = new_name; + dentry->d_name.name = old_name; +} + +static struct dentry * +shmem_iopen_lookup(struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode; + unsigned long ino; + struct list_head *lp; + struct dentry *alternate; + char buf[SHMEM_IOPEN_NAME_LEN]; + struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb); + + if (dentry->d_name.len >= SHMEM_IOPEN_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + memcpy(buf, dentry->d_name.name, dentry->d_name.len); + buf[dentry->d_name.len] = 0; + + if (strcmp(buf, ".") == 0) + ino = dir->i_ino; + else if (strcmp(buf, "..") == 0) + ino = sbinfo->root_ino; + else + ino = simple_strtoul(buf, 0, 0); + + if (ino < sbinfo->root_ino) + return ERR_PTR(-ENOENT); + + if (!(inode = shmem_find_inode(dir->i_sb, ino))) + return ERR_PTR(-ENOENT); + + ASSERT(list_empty(&dentry->d_alias)); + ASSERT(list_empty(&dentry->d_hash)); + + /* preferrably return a connected dentry */ + spin_lock(&dcache_lock); + list_for_each(lp, &inode->i_dentry) { + alternate = list_entry(lp, struct dentry, d_alias); + ASSERT(!(alternate->d_flags & DCACHE_NFSD_DISCONNECTED)); + } + + if (!list_empty(&inode->i_dentry)) { + alternate = list_entry(inode->i_dentry.next, + struct dentry, d_alias); + dget_locked(alternate); + alternate->d_vfs_flags |= DCACHE_REFERENCED; + iput(inode); + spin_unlock(&dcache_lock); + return alternate; + } + dentry->d_flags |= DCACHE_NFSD_DISCONNECTED; + + /* d_add(), but don't drop dcache_lock before adding dentry to inode */ + list_add(&dentry->d_alias, &inode->i_dentry); + dentry->d_inode = inode; + + __d_rehash(dentry, 0); + spin_unlock(&dcache_lock); + + return NULL; +} + +struct dentry * +shmem_iopen_unalias(struct dentry *dentry, struct inode *inode) +{ + struct dentry *tmp, *goal = NULL; + struct list_head *lp; + + list_for_each(lp, &inode->i_dentry) { + tmp = list_entry(lp, struct dentry, d_alias); + if (tmp->d_flags & DCACHE_NFSD_DISCONNECTED) { + ASSERT(tmp->d_alias.next == &inode->i_dentry); + ASSERT(tmp->d_alias.prev == &inode->i_dentry); + goal = tmp; + dget_locked(goal); + break; + } + } + + if (!goal) + return NULL; + + goal->d_flags &= ~DCACHE_NFSD_DISCONNECTED; + list_del_init(&goal->d_hash); + + list_del(&goal->d_child); + list_del(&dentry->d_child); + + switch_names(goal, dentry); + switch_fields(goal->d_parent, dentry->d_parent); + switch_fields(goal->d_name.len, dentry->d_name.len); + switch_fields(goal->d_name.hash, dentry->d_name.hash); + + list_add(&goal->d_child, &goal->d_parent->d_subdirs); + list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); + __d_rehash(goal, 0); + + return goal; +} + +static struct inode_operations iopen_inode_operations = { + lookup: shmem_iopen_lookup, +}; + +static void +shmem_delete_inode(struct inode *inode) { + struct dentry *dentry; + #ifdef CONFIG_TMPFS_XATTR struct list_head *tmp, *p; struct shmem_xattr *xattr; @@ -706,16 +887,30 @@ static void shmem_delete_inode(struct in #endif struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); - if (inode->i_op->truncate == shmem_truncate) { + if (inode->i_ino != SHMEM_IOPEN_INO) { + /* eliminating iopen alias */ + spin_lock(&dcache_lock); + if (!list_empty(&inode->i_dentry)) { + dentry = list_entry(inode->i_dentry.next, + struct dentry, d_alias); + shmem_iopen_unalias(dentry, inode); + } + spin_unlock(&dcache_lock); + spin_lock (&shmem_ilock); list_del (&SHMEM_I(inode)->list); spin_unlock (&shmem_ilock); - inode->i_size = 0; - shmem_truncate (inode); + + if (inode->i_op->truncate == shmem_truncate) { + inode->i_size = 0; + shmem_truncate (inode); + } + + spin_lock (&sbinfo->stat_lock); + sbinfo->free_inodes++; + spin_unlock (&sbinfo->stat_lock); } - spin_lock (&sbinfo->stat_lock); - sbinfo->free_inodes++; - spin_unlock (&sbinfo->stat_lock); + #ifdef CONFIG_TMPFS_XATTR list_for_each_safe(p, tmp, &info->xattrs) { xattr = list_entry(p, struct shmem_xattr, list); @@ -725,7 +920,8 @@ static void shmem_delete_inode(struct in clear_inode(inode); } -static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *ptr, swp_entry_t *eptr) +static inline int +shmem_find_swp(swp_entry_t entry, swp_entry_t *ptr, swp_entry_t *eptr) { swp_entry_t *test; @@ -736,7 +932,9 @@ static inline int shmem_find_swp(swp_ent return -1; } -static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page) +static int +shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, + struct page *page) { swp_entry_t *ptr; unsigned long idx; @@ -875,9 +1073,11 @@ getswap: * still need to guard against racing with shm_writepage(), which might * be trying to move the page to the swap cache as we run. */ -static struct page * shmem_getpage_locked(struct shmem_inode_info *info, struct inode * inode, unsigned long idx) +struct page * +shmem_getpage_locked(struct inode *inode, unsigned long idx) { struct address_space * mapping = inode->i_mapping; + struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_sb_info *sbinfo; struct page * page; swp_entry_t *entry; @@ -941,7 +1141,8 @@ repeat: swap_free(*entry); *entry = (swp_entry_t) {0}; delete_from_swap_cache(page); - flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced) | (1 << PG_arch_1)); + flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | + (1 << PG_referenced) | (1 << PG_arch_1)); page->flags = flags | (1 << PG_dirty); add_to_page_cache_locked(page, mapping, idx); info->swapped--; @@ -985,46 +1186,53 @@ wait_retry: goto repeat; } -int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr) +struct page * +shmem_getpage_unlocked(struct inode *inode, unsigned long idx) { + struct page *page; struct shmem_inode_info *info = SHMEM_I(inode); - int error; - down (&info->sem); - *ptr = ERR_PTR(-EFAULT); - if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE) + down(&info->sem); + page = ERR_PTR(-EFAULT); + + if (inode->i_size <= (loff_t)idx * PAGE_CACHE_SIZE) goto failed; - *ptr = shmem_getpage_locked(info, inode, idx); - if (IS_ERR (*ptr)) + page = shmem_getpage_locked(inode, idx); + + if (IS_ERR(page)) goto failed; - UnlockPage(*ptr); - up (&info->sem); - return 0; + UnlockPage(page); + up(&info->sem); + return page; failed: - up (&info->sem); - error = PTR_ERR(*ptr); - *ptr = NOPAGE_SIGBUS; - if (error == -ENOMEM) - *ptr = NOPAGE_OOM; - return error; + up(&info->sem); + + if (PTR_ERR(page) == -ENOMEM) + return NOPAGE_OOM; + + return page; } -struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused) +struct page * +shmem_nopage(struct vm_area_struct *vma, + unsigned long address, int unused) { - struct page * page; unsigned int idx; + struct page * page; struct inode * inode = vma->vm_file->f_dentry->d_inode; - idx = (address - vma->vm_start) >> PAGE_CACHE_SHIFT; - idx += vma->vm_pgoff; + idx = ((address - vma->vm_start) >> PAGE_CACHE_SHIFT) + + vma->vm_pgoff; - if (shmem_getpage(inode, idx, &page)) + page = shmem_getpage_unlocked(inode, idx); + + if (IS_ERR(page)) return page; flush_page_to_ram(page); - return(page); + return page; } void shmem_lock(struct file * file, int lock) @@ -1037,7 +1245,8 @@ void shmem_lock(struct file * file, int up(&info->sem); } -static int shmem_mmap(struct file * file, struct vm_area_struct * vma) +static int +shmem_mmap(struct file * file, struct vm_area_struct * vma) { struct vm_operations_struct * ops; struct inode *inode = file->f_dentry->d_inode; @@ -1050,39 +1259,53 @@ static int shmem_mmap(struct file * file return 0; } -struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev) +static void +shmem_fill_inode(struct inode *inode, int mode, int dev) { - struct inode * inode; struct shmem_inode_info *info; - struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + + info = SHMEM_I(inode); + info->inode = inode; + spin_lock_init (&info->lock); + sema_init (&info->sem, 1); - spin_lock (&sbinfo->stat_lock); - if (!sbinfo->free_inodes) { - spin_unlock (&sbinfo->stat_lock); - return NULL; - } - sbinfo->free_inodes--; - spin_unlock (&sbinfo->stat_lock); +#ifdef CONFIG_TMPFS_XATTR + INIT_LIST_HEAD(&info->xattrs); + info->xtail = &info->xattrs; +#endif - inode = new_inode(sb); - if (inode) { + inode->i_blocks = 0; + inode->i_rdev = NODEV; + inode->i_atime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME; + inode->i_mtime = CURRENT_TIME; + inode->i_blksize = PAGE_CACHE_SIZE; + + /* handling speciall iopen inode. */ + if (inode->i_ino == SHMEM_IOPEN_INO) { + struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); + + inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR; + + if (sbinfo->options & SHMEM_MOUNT_IOPEN_NOPRIV) + inode->i_mode |= 0777; + + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_nlink = 1; + inode->i_size = 2 * BOGO_DIRENT_SIZE; + inode->i_version = 1; + inode->i_generation = 0; + + inode->i_op = &iopen_inode_operations; + inode->i_fop = &dcache_dir_ops; + inode->i_mapping->a_ops = 0; + } else { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; - inode->i_blksize = PAGE_CACHE_SIZE; - inode->i_blocks = 0; - inode->i_rdev = NODEV; inode->i_mapping->a_ops = &shmem_aops; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - info = SHMEM_I(inode); - info->inode = inode; - spin_lock_init (&info->lock); - sema_init (&info->sem, 1); -#ifdef CONFIG_TMPFS_XATTR - INIT_LIST_HEAD(&info->xattrs); - info->xtail = &info->xattrs; -#endif switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); @@ -1090,9 +1313,6 @@ struct inode *shmem_get_inode(struct sup case S_IFREG: inode->i_op = &shmem_inode_operations; inode->i_fop = &shmem_file_operations; - spin_lock (&shmem_ilock); - list_add_tail(&info->list, &shmem_inodes); - spin_unlock (&shmem_ilock); break; case S_IFDIR: inode->i_nlink++; @@ -1104,12 +1324,59 @@ struct inode *shmem_get_inode(struct sup case S_IFLNK: break; } + + spin_lock (&shmem_ilock); + list_add_tail(&info->list, &shmem_inodes); + spin_unlock (&shmem_ilock); + } +} + +struct inode * +shmem_get_inode(struct super_block *sb, + int mode, int dev, int root) +{ + struct inode *inode; + struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + + spin_lock (&sbinfo->stat_lock); + if (!sbinfo->free_inodes) { + spin_unlock (&sbinfo->stat_lock); + return NULL; + } + sbinfo->free_inodes--; + spin_unlock (&sbinfo->stat_lock); + + if ((inode = new_inode(sb))) { + shmem_fill_inode(inode, mode, dev); + if (root) + sbinfo->root_ino = inode->i_ino; } + return inode; } -static int shmem_set_size(struct shmem_sb_info *info, - unsigned long max_blocks, unsigned long max_inodes) +void shmem_read_inode(struct inode *inode) +{ + struct shmem_sb_info *sbinfo; + + if (inode->i_ino != SHMEM_IOPEN_INO) + return; + + sbinfo = SHMEM_SB(inode->i_sb); + + spin_lock (&sbinfo->stat_lock); + if (!sbinfo->free_inodes) { + spin_unlock (&sbinfo->stat_lock); + return; + } + sbinfo->free_inodes--; + spin_unlock (&sbinfo->stat_lock); + shmem_fill_inode(inode, 0, 0); +} + +static int +shmem_set_size(struct shmem_sb_info *info, unsigned long max_blocks, + unsigned long max_inodes) { int error; unsigned long blocks, inodes; @@ -1192,7 +1459,6 @@ shmem_file_write(struct file *file,const while (count) { unsigned long bytes, index, offset; - char *kaddr; /* * Try to find the page in the cache. If it isn't there, @@ -1201,9 +1467,9 @@ shmem_file_write(struct file *file,const offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */ index = pos >> PAGE_CACHE_SHIFT; bytes = PAGE_CACHE_SIZE - offset; - if (bytes > count) { + + if (bytes > count) bytes = count; - } /* * Bring in the user page that we will copy from _first_. @@ -1218,7 +1484,7 @@ shmem_file_write(struct file *file,const info = SHMEM_I(inode); down (&info->sem); - page = shmem_getpage_locked(info, inode, index); + page = shmem_getpage_locked(inode, index); up (&info->sem); status = PTR_ERR(page); @@ -1226,17 +1492,19 @@ shmem_file_write(struct file *file,const break; /* We have exclusive IO access to the page.. */ - if (!PageLocked(page)) { + if (!PageLocked(page)) PAGE_BUG(page); - } - kaddr = kmap(page); - status = copy_from_user(kaddr+offset, buf, bytes); + status = copy_from_user(kmap(page) + offset, + buf, bytes); + kunmap(page); + if (status) goto fail_write; flush_dcache_page(page); + if (bytes > 0) { SetPageDirty(page); written += bytes; @@ -1266,7 +1534,8 @@ fail_write: goto unlock; } -static void do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc) +static void +do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc) { struct inode *inode = filp->f_dentry->d_inode; struct address_space *mapping = inode->i_mapping; @@ -1292,15 +1561,18 @@ static void do_shmem_file_read(struct fi nr = nr - offset; - if ((desc->error = shmem_getpage(inode, index, &page))) + page = shmem_getpage_unlocked(inode, index); + + if (IS_ERR(page)) { + desc->error = PTR_ERR(page); break; + } if (mapping->i_mmap_shared != NULL) flush_dcache_page(page); - /* - * Ok, we have the page, and it's up-to-date, so - * now we can copy it to user space... + /* Ok, we have the page, and it's up-to-date, so now we can copy + * it to user space... * * The actor routine returns how many bytes were actually used.. * NOTE! This may not be the same as how much of a user buffer @@ -1309,6 +1581,8 @@ static void do_shmem_file_read(struct fi * pointers and the remaining count). */ nr = file_read_actor(desc, page, offset, nr); + + /* updating counters */ offset += nr; index += offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; @@ -1320,7 +1594,8 @@ static void do_shmem_file_read(struct fi UPDATE_ATIME(inode); } -static ssize_t shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos) +static ssize_t +shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos) { ssize_t retval; @@ -1345,7 +1620,8 @@ static ssize_t shmem_file_read(struct fi return retval; } -static int shmem_statfs(struct super_block *sb, struct statfs *buf) +static int +shmem_statfs(struct super_block *sb, struct statfs *buf) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); @@ -1361,22 +1637,62 @@ static int shmem_statfs(struct super_blo return 0; } -/* - * Lookup the data. This is trivial - if the dentry didn't already - * exist, we know it is negative. - */ -static struct dentry * shmem_lookup(struct inode *dir, struct dentry *dentry) +static int +match_dentry(struct dentry *dentry, const char *name) +{ + int len = strlen(name); + + if (dentry->d_name.len != len) + return 0; + + if (strncmp(dentry->d_name.name, name, len)) + return 0; + + return 1; +} + +static int +shmem_iopen_check(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb); + + if (dir->i_ino != sbinfo->root_ino || + !(sbinfo->options & SHMEM_MOUNT_IOPEN) || + !match_dentry(dentry, "__iopen__")) + { + return 0; + } + + if (!(inode = iget(dir->i_sb, SHMEM_IOPEN_INO))) + return 0; + + d_add(dentry, inode); + + spin_lock (&sbinfo->stat_lock); + sbinfo->iopen = dentry; + spin_unlock (&sbinfo->stat_lock); + + dget(dentry); + return 1; +} + +static struct dentry * +shmem_lookup(struct inode *dir, struct dentry *dentry) { - d_add(dentry, NULL); + if (!shmem_iopen_check(dir, dentry)) + d_add(dentry, NULL); + return NULL; } /* * File creation. Allocate an inode, and we're done.. */ -static int shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) +static int +shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) { - struct inode * inode = shmem_get_inode(dir->i_sb, mode, dev); + struct inode *inode = shmem_get_inode(dir->i_sb, mode, dev, 0); int error = -ENOSPC; if (inode) { @@ -1386,20 +1702,24 @@ static int shmem_mknod(struct inode *dir dget(dentry); /* Extra count - pin the dentry in core */ error = 0; } + return error; } -static int shmem_mkdir(struct inode * dir, struct dentry * dentry, int mode) +static int +shmem_mkdir(struct inode * dir, struct dentry * dentry, int mode) { int error; if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0))) return error; + dir->i_nlink++; return 0; } -static int shmem_create(struct inode *dir, struct dentry *dentry, int mode) +static int +shmem_create(struct inode *dir, struct dentry *dentry, int mode) { return shmem_mknod(dir, dentry, mode | S_IFREG, 0); } @@ -1407,7 +1727,8 @@ static int shmem_create(struct inode *di /* * Link a file.. */ -static int shmem_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry) +static int +shmem_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry) { struct inode *inode = old_dentry->d_inode; @@ -1429,13 +1750,11 @@ static inline int shmem_positive(struct } /* - * Check that a directory is empty (this works - * for regular files too, they'll just always be - * considered empty..). + * Check that a directory is empty (this works for regular files too, they'll + * just always be considered empty..). * - * Note that an empty directory can still have - * children, they just all have to be negative.. - */ + * Note that an empty directory can still have children, they just all have to + * be negative.. */ static int shmem_empty(struct dentry *dentry) { struct list_head *list; @@ -1456,18 +1775,22 @@ static int shmem_empty(struct dentry *de return 1; } -static int shmem_unlink(struct inode * dir, struct dentry *dentry) +static int +shmem_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; dir->i_size -= BOGO_DIRENT_SIZE; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inode->i_nlink--; - dput(dentry); /* Undo the count from "create" - this does all the work */ + + /* undo the count from "create" - this does all the work. */ + dput(dentry); return 0; } -static int shmem_rmdir(struct inode * dir, struct dentry *dentry) +static int +shmem_rmdir(struct inode *dir, struct dentry *dentry) { if (!shmem_empty(dentry)) return -ENOTEMPTY; @@ -1477,12 +1800,13 @@ static int shmem_rmdir(struct inode * di } /* - * The VFS layer already does all the dentry stuff for rename, - * we just have to decrement the usage count for the target if - * it exists so that the VFS layer correctly free's it when it - * gets overwritten. + * The VFS layer already does all the dentry stuff for rename, we just have to + * decrement the usage count for the target if it exists so that the VFS layer + * correctly free's it when it gets overwritten. */ -static int shmem_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) +static int +shmem_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { struct inode *inode = old_dentry->d_inode; int they_are_dirs = S_ISDIR(inode->i_mode); @@ -1507,19 +1831,20 @@ static int shmem_rename(struct inode * o return 0; } -static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname) +static int +shmem_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) { int len; - struct inode *inode; struct page *page; - char *kaddr; - struct shmem_inode_info * info; + struct inode *inode; + struct shmem_inode_info *info; len = strlen(symname) + 1; if (len > PAGE_CACHE_SIZE) return -ENAMETOOLONG; - inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0); + inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0, 0); if (!inode) return -ENOSPC; @@ -1531,7 +1856,7 @@ static int shmem_symlink(struct inode * inode->i_op = &shmem_symlink_inline_operations; } else { down(&info->sem); - page = shmem_getpage_locked(info, inode, 0); + page = shmem_getpage_locked(inode, 0); if (IS_ERR(page)) { up(&info->sem); iput(inode); @@ -1541,8 +1866,7 @@ static int shmem_symlink(struct inode * spin_lock (&shmem_ilock); list_add_tail(&info->list, &shmem_inodes); spin_unlock (&shmem_ilock); - kaddr = kmap(page); - memcpy(kaddr, symname, len); + memcpy(kmap(page), symname, len); kunmap(page); SetPageDirty(page); UnlockPage(page); @@ -1556,40 +1880,52 @@ static int shmem_symlink(struct inode * return 0; } -static int shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen) +static int +shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen) { - return vfs_readlink(dentry,buffer,buflen, (const char *)SHMEM_I(dentry->d_inode)); + return vfs_readlink(dentry,buffer, buflen, + (const char *)SHMEM_I(dentry->d_inode)); } -static int shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) +static int +shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) { return vfs_follow_link(nd, (const char *)SHMEM_I(dentry->d_inode)); } -static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen) +static int +shmem_readlink(struct dentry *dentry, char *buffer, int buflen) { - struct page * page; - int res = shmem_getpage(dentry->d_inode, 0, &page); + int res; + struct page *page; + + page = shmem_getpage_unlocked(dentry->d_inode, 0); - if (res) - return res; + if (IS_ERR(page)) + return PTR_ERR(page); res = vfs_readlink(dentry,buffer,buflen, kmap(page)); kunmap(page); page_cache_release(page); + return res; } -static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd) +static int +shmem_follow_link(struct dentry *dentry, struct nameidata *nd) { struct page * page; - int res = shmem_getpage(dentry->d_inode, 0, &page); - if (res) - return res; + int res; + + page = shmem_getpage_unlocked(dentry->d_inode, 0); + + if (IS_ERR(page)) + return PTR_ERR(page); res = vfs_follow_link(nd, kmap(page)); kunmap(page); page_cache_release(page); + return res; } @@ -1610,7 +1946,10 @@ static struct inode_operations shmem_sym #endif }; -static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long * blocks, unsigned long *inodes) +static int +shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, + unsigned long *blocks, unsigned long *inodes, + unsigned long *opts) { char *this_char, *value, *rest; @@ -1620,11 +1959,6 @@ static int shmem_parse_options(char *opt for ( ; this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) { *value++ = 0; - } else { - printk(KERN_ERR - "tmpfs: No value for mount option '%s'\n", - this_char); - return 1; } if (!strcmp(this_char,"size")) { @@ -1659,6 +1993,17 @@ static int shmem_parse_options(char *opt *gid = simple_strtoul(value,&rest,0); if (*rest) goto bad_val; + } else if (!strcmp(this_char, "iopen")) { + *opts |= SHMEM_MOUNT_IOPEN; + *opts &= ~SHMEM_MOUNT_IOPEN_NOPRIV; + } + else if (!strcmp(this_char, "noiopen")) { + *opts &= ~SHMEM_MOUNT_IOPEN; + *opts &= ~SHMEM_MOUNT_IOPEN_NOPRIV; + } + else if (!strcmp (this_char, "iopen_nopriv")) { + *opts |= SHMEM_MOUNT_IOPEN; + *opts |= SHMEM_MOUNT_IOPEN_NOPRIV; } else { printk(KERN_ERR "tmpfs: Bad mount option %s\n", this_char); @@ -1674,14 +2019,19 @@ bad_val: } -static int shmem_remount_fs (struct super_block *sb, int *flags, char *data) +static int +shmem_remount_fs(struct super_block *sb, int *flags, char *data) { struct shmem_sb_info *sbinfo = &sb->u.shmem_sb; unsigned long max_blocks = sbinfo->max_blocks; unsigned long max_inodes = sbinfo->max_inodes; - if (shmem_parse_options (data, NULL, NULL, NULL, &max_blocks, &max_inodes)) + if (shmem_parse_options (data, NULL, NULL, NULL, &max_blocks, + &max_inodes, &sbinfo->options)) + { return -EINVAL; + } + return shmem_set_size(sbinfo, max_blocks, max_inodes); } @@ -1691,7 +2041,8 @@ int shmem_sync_file(struct file * file, } #endif -static struct super_block *shmem_read_super(struct super_block * sb, void * data, int silent) +static struct super_block * +shmem_read_super(struct super_block * sb, void * data, int silent) { struct inode * inode; struct dentry * root; @@ -1710,11 +2061,15 @@ static struct super_block *shmem_read_su blocks = inodes = si.totalram / 2; #ifdef CONFIG_TMPFS - if (shmem_parse_options (data, &mode, &uid, &gid, &blocks, &inodes)) + if (shmem_parse_options (data, &mode, &uid, &gid, &blocks, + &inodes, &sbinfo->options)) + { return NULL; + } #endif spin_lock_init (&sbinfo->stat_lock); + sbinfo->iopen = NULL; sbinfo->max_blocks = blocks; sbinfo->free_blocks = blocks; sbinfo->max_inodes = inodes; @@ -1724,7 +2079,7 @@ static struct super_block *shmem_read_su sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = TMPFS_MAGIC; sb->s_op = &shmem_ops; - inode = shmem_get_inode(sb, S_IFDIR | mode, 0); + inode = shmem_get_inode(sb, S_IFDIR | mode, 0, 1); if (!inode) return NULL; @@ -1739,7 +2094,19 @@ static struct super_block *shmem_read_su return sb; } +void shmem_put_super(struct super_block *sb) +{ + struct dentry *iopen; + struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + spin_lock(&sbinfo->stat_lock); + iopen = sbinfo->iopen; + sbinfo->iopen = NULL; + spin_unlock(&sbinfo->stat_lock); + + if (iopen) + dput(iopen); +} static struct address_space_operations shmem_aops = { writepage: shmem_writepage, @@ -1790,11 +2157,13 @@ static struct super_operations shmem_ops remount_fs: shmem_remount_fs, #endif delete_inode: shmem_delete_inode, - put_inode: force_delete, + read_inode: shmem_read_inode, + put_inode: force_delete, + put_super: shmem_put_super, }; static struct vm_operations_struct shmem_vm_ops = { - nopage: shmem_nopage, + nopage: shmem_nopage, }; #ifdef CONFIG_TMPFS @@ -1885,7 +2254,7 @@ struct file *shmem_file_setup(char * nam goto put_dentry; error = -ENOSPC; - inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0); + inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0, 0); if (!inode) goto close_file; @@ -1921,15 +2290,17 @@ int shmem_zero_setup(struct vm_area_stru if (vma->vm_file) fput (vma->vm_file); + vma->vm_file = file; vma->vm_ops = &shmem_vm_ops; return 0; } EXPORT_SYMBOL(shmem_file_setup); -EXPORT_SYMBOL(shmem_getpage); EXPORT_SYMBOL(shmem_xattr_find); EXPORT_SYMBOL(shmem_xattr_set); EXPORT_SYMBOL(shmem_xattr_get); EXPORT_SYMBOL(shmem_xattr_delete); EXPORT_SYMBOL(shmem_xattr_remove); +EXPORT_SYMBOL(shmem_getpage_locked); +EXPORT_SYMBOL(shmem_getpage_unlocked);