diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
commit | 529c593ece216e4aaffd36bd940cb94f1fa63129 (patch) | |
tree | 78f1c0b805f5656aa7b0417a043c5346f700a2cf /fs | |
parent | 0bd079751d25808d1972baee5c4eaa1db2227257 (diff) |
Merge with 2.3.43. I did ignore all modifications to the qlogicisp.c
driver due to the Origin A64 hacks.
Diffstat (limited to 'fs')
159 files changed, 3957 insertions, 4125 deletions
diff --git a/fs/Config.in b/fs/Config.in index 085e191cb..934a7a70a 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -15,8 +15,7 @@ tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS dep_tristate 'Apple Macintosh filesystem support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL -dep_tristate 'BFS filesystem (read only) support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL -dep_bool ' BFS filesystem write support (DANGEROUS)' CONFIG_BFS_FS_WRITE $CONFIG_BFS_FS +dep_tristate 'BFS filesystem support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL # msdos filesystems tristate 'DOS FAT fs support' CONFIG_FAT_FS diff --git a/fs/adfs/Makefile b/fs/adfs/Makefile index 0764629fe..6ce002004 100644 --- a/fs/adfs/Makefile +++ b/fs/adfs/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := adfs.o -O_OBJS := dir.o file.o inode.o map.o namei.o super.o +O_OBJS := dir.o dir_f.o dir_fplus.o file.o inode.o map.o super.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h new file mode 100644 index 000000000..abda2e2d0 --- /dev/null +++ b/fs/adfs/adfs.h @@ -0,0 +1,134 @@ +/* Internal data structures for ADFS */ + +#define ADFS_FREE_FRAG 0 +#define ADFS_BAD_FRAG 1 +#define ADFS_ROOT_FRAG 2 + +#define ADFS_NDA_OWNER_READ (1 << 0) +#define ADFS_NDA_OWNER_WRITE (1 << 1) +#define ADFS_NDA_LOCKED (1 << 2) +#define ADFS_NDA_DIRECTORY (1 << 3) +#define ADFS_NDA_EXECUTE (1 << 4) +#define ADFS_NDA_PUBLIC_READ (1 << 5) +#define ADFS_NDA_PUBLIC_WRITE (1 << 6) + +#include "dir_f.h" + +/* + * Directory handling + */ +struct adfs_dir { + struct super_block *sb; + + int nr_buffers; + struct buffer_head *bh[4]; + unsigned int pos; + unsigned int parent_id; + + struct adfs_dirheader dirhead; + union adfs_dirtail dirtail; +}; + +/* + * This is the overall maximum name length + */ +#define ADFS_MAX_NAME_LEN 256 +struct object_info { + __u32 parent_id; /* parent object id */ + __u32 file_id; /* object id */ + __u32 loadaddr; /* load address */ + __u32 execaddr; /* execution address */ + __u32 size; /* size */ + __u8 attr; /* RISC OS attributes */ + unsigned char name_len; /* name length */ + char name[ADFS_MAX_NAME_LEN];/* file name */ +}; + +struct adfs_dir_ops { + int (*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir); + int (*setpos)(struct adfs_dir *dir, unsigned int fpos); + int (*getnext)(struct adfs_dir *dir, struct object_info *obj); + int (*update)(struct adfs_dir *dir, struct object_info *obj); + int (*create)(struct adfs_dir *dir, struct object_info *obj); + int (*remove)(struct adfs_dir *dir, struct object_info *obj); + void (*free)(struct adfs_dir *dir); +}; + +struct adfs_discmap { + struct buffer_head *dm_bh; + __u32 dm_startblk; + unsigned int dm_startbit; + unsigned int dm_endbit; +}; + +/* dir stuff */ + + +/* Inode stuff */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +int adfs_get_block(struct inode *inode, long block, + struct buffer_head *bh, int create); +#else +int adfs_bmap(struct inode *inode, int block); +#endif +struct inode *adfs_iget(struct super_block *sb, struct object_info *obj); +void adfs_read_inode(struct inode *inode); +void adfs_write_inode(struct inode *inode); +int adfs_notify_change(struct dentry *dentry, struct iattr *attr); + +/* map.c */ +extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset); +extern unsigned int adfs_map_free(struct super_block *sb); + +/* Misc */ +void __adfs_error(struct super_block *sb, const char *function, + const char *fmt, ...); +#define adfs_error(sb, fmt...) __adfs_error(sb, __FUNCTION__, fmt) + +/* namei.c */ +extern struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry); + +/* super.c */ + +/* + * Inodes and file operations + */ + +/* dir_*.c */ +extern struct inode_operations adfs_dir_inode_operations; +extern struct adfs_dir_ops adfs_f_dir_ops; +extern struct adfs_dir_ops adfs_fplus_dir_ops; + +extern int adfs_dir_update(struct super_block *sb, struct object_info *obj); + +/* file.c */ +extern struct inode_operations adfs_file_inode_operations; + +extern inline __u32 signed_asl(__u32 val, signed int shift) +{ + if (shift >= 0) + val <<= shift; + else + val >>= -shift; + return val; +} + +/* + * Calculate the address of a block in an object given the block offset + * and the object identity. + * + * The root directory ID should always be looked up in the map [3.4] + */ +extern inline int +__adfs_block_map(struct super_block *sb, unsigned int object_id, + unsigned int block) +{ + if (object_id & 255) { + unsigned int off; + + off = (object_id & 255) - 1; + block += off << sb->u.adfs_sb.s_log2sharesize; + } + + return adfs_map_lookup(sb, object_id >> 8, block); +} diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index a65f5c168..140e28598 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -1,345 +1,302 @@ /* - * linux/fs/adfs/dir.c + * linux/fs/adfs/dir.c * - * Copyright (C) 1997 Russell King + * Copyright (C) 1999 Russell King + * + * Common directory handling for ADFS */ - +#include <linux/version.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/adfs_fs.h> #include <linux/sched.h> #include <linux/stat.h> -static ssize_t adfs_dirread (struct file *filp, char *buf, - size_t siz, loff_t *ppos) -{ - return -EISDIR; -} - -static int adfs_readdir (struct file *, void *, filldir_t); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +#include <linux/spinlock.h> +#else +#include <asm/spinlock.h> +#endif -static struct file_operations adfs_dir_operations = { - NULL, /* lseek - default */ - adfs_dirread, /* read */ - NULL, /* write - bad */ - adfs_readdir, /* readdir */ - NULL, /* select - default */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync, /* fsync */ - NULL, /* fasync */ -}; +#include "adfs.h" /* - * directories can handle most operations... + * For future. This should probably be per-directory. */ -struct inode_operations adfs_dir_inode_operations = { - &adfs_dir_operations, /* default directory file-ops */ - NULL, /* create */ - adfs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* read link */ - NULL, /* follow link */ - NULL, /* get_block */ - NULL, /* read page */ - NULL, /* write page */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ -}; +static rwlock_t adfs_dir_lock; -unsigned int adfs_val (unsigned char *p, int len) +static int +adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { - unsigned int val = 0; - - switch (len) { - case 4: - val |= p[3] << 24; - case 3: - val |= p[2] << 16; - case 2: - val |= p[1] << 8; + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = filp->f_dentry->d_sb; + struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir; + struct object_info obj; + struct adfs_dir dir; + int ret; + + ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); + if (ret) + goto out; + + switch (filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) + goto free_out; + filp->f_pos += 1; + + case 1: + if (filldir(dirent, "..", 2, 1, dir.parent_id) < 0) + goto free_out; + filp->f_pos += 1; + default: - val |= p[0]; + break; } - return val; -} - -static unsigned int adfs_filetype (unsigned int load) -{ - if ((load & 0xfff00000) != 0xfff00000) - return (unsigned int) -1; - return (load >> 8) & 0xfff; -} -static unsigned int adfs_time (unsigned int load, unsigned int exec) -{ - unsigned int high, low; - - /* Check for unstamped files. */ - if ((load & 0xfff00000) != 0xfff00000) - return 0; + read_lock(&adfs_dir_lock); - high = ((load << 24) | (exec >> 8)); - low = exec & 255; - - /* Files dated pre 1970. */ - if (high < 0x336e996a) - return 0; + ret = ops->setpos(&dir, filp->f_pos - 2); + if (ret) + goto unlock_out; + while (ops->getnext(&dir, &obj) == 0) { + if (filldir(dirent, obj.name, obj.name_len, + filp->f_pos, obj.file_id) < 0) + goto unlock_out; + filp->f_pos += 1; + } - high -= 0x336e996a; +unlock_out: + read_unlock(&adfs_dir_lock); - /* Files dated post 2038 ish. */ - if (high > 0x31ffffff) - return 0x7fffffff; +free_out: + ops->free(&dir); - /* 65537 = h256,l1 - * (h256 % 100) = 56 h256 / 100 = 2 - * 56 << 8 = 14336 2 * 256 = 512 - * + l1 = 14337 - * / 100 = 143 - * + 512 = 655 - */ - return (((high % 100) << 8) + low) / 100 + (high / 100 << 8); +out: + return ret; } -int adfs_readname (char *buf, char *ptr, int maxlen) +int +adfs_dir_update(struct super_block *sb, struct object_info *obj) { - int size = 0; - while (*ptr >= ' ' && maxlen--) { - switch (*ptr) { - case '/': - *buf++ = '.'; - break; - default: - *buf++ = *ptr; - break; - } - ptr++; - size ++; + struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir; + struct adfs_dir dir; + int ret = -EINVAL; + + printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n", + obj->file_id, obj->parent_id); +#if 0 + if (!ops->update) { + ret = -EINVAL; + goto out; } - *buf = '\0'; - return size; -} -int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp) -{ - struct super_block *sb; - int i, size; - - sb = inode->i_sb; - - size = 2048 >> sb->s_blocksize_bits; - - for (i = 0; i < size; i++) { - int block; - - block = adfs_parent_bmap (inode, i); - if (block) - bhp[i] = bread (sb->s_dev, block, sb->s_blocksize); - else - adfs_error (sb, "adfs_dir_read_parent", - "directory %lu with a hole at offset %d", inode->i_ino, i); - if (!block || !bhp[i]) { - int j; - for (j = i - 1; j >= 0; j--) - brelse (bhp[j]); - return 0; - } - } - return i; + ret = ops->read(sb, obj->parent_id, 0, &dir); + if (ret) + goto out; + + write_lock(&adfs_dir_lock); + ret = ops->update(&dir, obj); + write_unlock(&adfs_dir_lock); + + ops->free(&dir); +out: +#endif + return ret; } -int adfs_dir_read (struct inode *inode, struct buffer_head **bhp) +static int +adfs_match(struct qstr *name, struct object_info *obj) { - struct super_block *sb; - int i, size; + int i; - if (!inode || !S_ISDIR(inode->i_mode)) + if (name->len != obj->name_len) return 0; - sb = inode->i_sb; + for (i = 0; i < name->len; i++) { + char c1, c2; - size = inode->i_size >> sb->s_blocksize_bits; + c1 = name->name[i]; + c2 = obj->name[i]; - for (i = 0; i < size; i++) { - int block; + if (c1 >= 'A' && c1 <= 'Z') + c1 += 'a' - 'A'; + if (c2 >= 'A' && c2 <= 'Z') + c2 += 'a' - 'A'; - block = adfs_bmap (inode, i); - if (block) - bhp[i] = bread (sb->s_dev, block, sb->s_blocksize); - else - adfs_error (sb, "adfs_dir_read", - "directory %lX,%lX with a hole at offset %d", - inode->i_ino, inode->u.adfs_i.file_id, i); - if (!block || !bhp[i]) { - int j; - for (j = i - 1; j >= 0; j--) - brelse (bhp[j]); + if (c1 != c2) return 0; - } } - return i; + return 1; } -int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp) +static int +adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj) { - struct adfs_dirheader dh; - union adfs_dirtail dt; - - memcpy (&dh, bhp[0]->b_data, sizeof (dh)); - memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt)); - - if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) || - (memcmp (&dh.startname, "Nick", 4) && - memcmp (&dh.startname, "Hugo", 4))) { - adfs_error (inode->i_sb, "adfs_check_dir", - "corrupted directory inode %lX,%lX", - inode->i_ino, inode->u.adfs_i.file_id); - return 1; + struct super_block *sb = inode->i_sb; + struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir; + struct adfs_dir dir; + int ret; + + ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); + if (ret) + goto out; + + if (inode->u.adfs_i.parent_id != dir.parent_id) { + adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n", + inode->u.adfs_i.parent_id, dir.parent_id); + ret = -EIO; + goto free_out; } - if (dtp) - *dtp = dt; - return 0; -} - -void adfs_dir_free (struct buffer_head **bhp, int buffers) -{ - int i; - - for (i = buffers - 1; i >= 0; i--) - brelse (bhp[i]); -} - -/* convert a disk-based directory entry to a Linux ADFS directory entry */ -static inline void -adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de) -{ - ide->name_len = adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN); - ide->file_id = adfs_val(de->dirinddiscadd, 3); - ide->size = adfs_val(de->dirlen, 4); - ide->mode = de->newdiratts; - ide->mtime = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4)); - ide->filetype = adfs_filetype(adfs_val(de->dirload, 4)); -} -int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp, - int buffers, int pos, unsigned long parent_object_id, - struct adfs_idir_entry *ide) -{ - struct adfs_direntry de; - int thissize, buffer, offset; + obj->parent_id = inode->i_ino; - offset = pos & (sb->s_blocksize - 1); - buffer = pos >> sb->s_blocksize_bits; + /* + * '.' is handled by reserved_lookup() in fs/namei.c + */ + if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') { + /* + * Currently unable to fill in the rest of 'obj', + * but this is better than nothing. We need to + * ascend one level to find it's parent. + */ + obj->name_len = 0; + obj->file_id = obj->parent_id; + goto free_out; + } - if (buffer > buffers) - return 0; + read_lock(&adfs_dir_lock); - thissize = sb->s_blocksize - offset; - if (thissize > 26) - thissize = 26; + ret = ops->setpos(&dir, 0); + if (ret) + goto unlock_out; - memcpy (&de, bhp[buffer]->b_data + offset, thissize); - if (thissize != 26) - memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize); + ret = -ENOENT; + while (ops->getnext(&dir, obj) == 0) { + if (adfs_match(name, obj)) { + ret = 0; + break; + } + } - if (!de.dirobname[0]) - return 0; +unlock_out: + read_unlock(&adfs_dir_lock); - ide->inode_no = adfs_inode_generate (parent_object_id, pos); - adfs_dirent_to_idirent(ide, &de); - return 1; +free_out: + ops->free(&dir); +out: + return ret; } -int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp, - int buffers, unsigned int pos, - struct adfs_idir_entry *ide) +static ssize_t +adfs_dir_no_read(struct file *filp, char *buf, size_t siz, loff_t *ppos) { - struct adfs_direntry de; - int offset, buffer, thissize; + return -EISDIR; +} - offset = pos & (sb->s_blocksize - 1); - buffer = pos >> sb->s_blocksize_bits; +static struct file_operations adfs_dir_operations = { + read: adfs_dir_no_read, + readdir: adfs_readdir, + fsync: file_fsync, +}; + +static int +adfs_hash(struct dentry *parent, struct qstr *qstr) +{ + const unsigned int name_len = parent->d_sb->u.adfs_sb.s_namelen; + const unsigned char *name; + unsigned long hash; + int i; - if (buffer > buffers) + if (qstr->len < name_len) return 0; - thissize = sb->s_blocksize - offset; - if (thissize > 26) - thissize = 26; + /* + * Truncate the name in place, avoids + * having to define a compare function. + */ + qstr->len = i = name_len; + name = qstr->name; + hash = init_name_hash(); + while (i--) { + char c; - memcpy (&de, bhp[buffer]->b_data + offset, thissize); - if (thissize != 26) - memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize); + c = *name++; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; - if (!de.dirobname[0]) - return 0; + hash = partial_name_hash(c, hash); + } + qstr->hash = end_name_hash(hash); - adfs_dirent_to_idirent(ide, &de); - return 1; -} + return 0; +} -static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir) +/* + * Compare two names, taking note of the name length + * requirements of the underlying filesystem. + */ +static int +adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name) { - struct inode *inode = filp->f_dentry->d_inode; - struct super_block *sb; - struct buffer_head *bh[4]; - union adfs_dirtail dt; - unsigned long parent_object_id, dir_object_id; - int buffers, pos; - - sb = inode->i_sb; + int i; - if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2) - return -ENOENT; + if (entry->len != name->len) + return 1; - if (!(buffers = adfs_dir_read (inode, bh))) { - adfs_error (sb, "adfs_readdir", "unable to read directory"); - return -EINVAL; - } + for (i = 0; i < name->len; i++) { + char a, b; - if (adfs_dir_check (inode, bh, buffers, &dt)) { - adfs_dir_free (bh, buffers); - return -ENOENT; - } + a = entry->name[i]; + b = name->name[i]; - parent_object_id = adfs_val (dt.new.dirparent, 3); - dir_object_id = adfs_inode_objid (inode); + if (a >= 'A' && a <= 'Z') + a += 'a' - 'A'; + if (b >= 'A' && b <= 'Z') + b += 'a' - 'A'; - if (filp->f_pos < 2) { - if (filp->f_pos < 1) { - if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0) - return 0; - filp->f_pos ++; - } - if (filldir (dirent, "..", 2, 1, - adfs_inode_generate (parent_object_id, 0)) < 0) - return 0; - filp->f_pos ++; + if (a != b) + return 1; } + return 0; +} - pos = 5 + (filp->f_pos - 2) * 26; - while (filp->f_pos < 79) { - struct adfs_idir_entry ide; - - if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide)) - break; +struct dentry_operations adfs_dentry_operations = { + NULL, /* revalidate */ + adfs_hash, + adfs_compare, + NULL, /* delete = called by dput */ + NULL, /* release - called by d_free */ + NULL /* iput - called by dentry_iput */ +}; - if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0) - return 0; - filp->f_pos ++; - pos += 26; +struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = NULL; + struct object_info obj; + int error; + + dentry->d_op = &adfs_dentry_operations; + error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); + if (error == 0) { + error = -EACCES; + /* + * This only returns NULL if get_empty_inode + * fails. + */ + inode = adfs_iget(dir->i_sb, &obj); + if (inode) + error = 0; } - adfs_dir_free (bh, buffers); - return 0; + d_add(dentry, inode); + return ERR_PTR(error); } + +/* + * directories can handle most operations... + */ +struct inode_operations adfs_dir_inode_operations = { + &adfs_dir_operations, /* default directory file-ops */ + NULL, /* create */ + adfs_lookup, /* lookup */ +}; diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c new file mode 100644 index 000000000..cec45c098 --- /dev/null +++ b/fs/adfs/dir_f.c @@ -0,0 +1,479 @@ +/* + * linux/fs/adfs/dir_f.c + * + * Copyright (C) 1997-1999 Russell King + * + * E and F format directory handling + */ +#include <linux/version.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/sched.h> +#include <linux/stat.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +#include <linux/spinlock.h> +#else +#include <asm/spinlock.h> +#endif + +#include "adfs.h" +#include "dir_f.h" + +static void adfs_f_free(struct adfs_dir *dir); + +/* + * Read an (unaligned) value of length 1..4 bytes + */ +static inline unsigned int adfs_readval(unsigned char *p, int len) +{ + unsigned int val = 0; + + switch (len) { + case 4: val |= p[3] << 24; + case 3: val |= p[2] << 16; + case 2: val |= p[1] << 8; + default: val |= p[0]; + } + return val; +} + +static inline void adfs_writeval(unsigned char *p, int len, unsigned int val) +{ + switch (len) { + case 4: p[3] = val >> 24; + case 3: p[2] = val >> 16; + case 2: p[1] = val >> 8; + default: p[0] = val; + } +} + +static inline int adfs_readname(char *buf, char *ptr, int maxlen) +{ + char *old_buf = buf; + + while (*ptr >= ' ' && maxlen--) { + if (*ptr == '/') + *buf++ = '.'; + else + *buf++ = *ptr; + ptr++; + } + *buf = '\0'; + + return buf - old_buf; +} + +static inline void adfs_writename(char *to, char *from, int maxlen) +{ + int i; + + for (i = 0; i < maxlen; i++) { + if (from[i] == '\0') + break; + if (from[i] == '.') + to[i] = '/'; + else + to[i] = from[i]; + } + + for (; i < maxlen; i++) + to[i] = '\0'; +} + +#define ror13(v) ((v >> 13) | (v << 19)) + +#define dir_u8(idx) \ + ({ int _buf = idx >> blocksize_bits; \ + int _off = idx - (_buf << blocksize_bits);\ + *(u8 *)(bh[_buf]->b_data + _off); \ + }) + +#define dir_u32(idx) \ + ({ int _buf = idx >> blocksize_bits; \ + int _off = idx - (_buf << blocksize_bits);\ + *(u32 *)(bh[_buf]->b_data + _off); \ + }) + +#define bufoff(_bh,_idx) \ + ({ int _buf = _idx >> blocksize_bits; \ + int _off = _idx - (_buf << blocksize_bits);\ + (u8 *)(_bh[_buf]->b_data + _off); \ + }) + +/* + * There are some algorithms that are nice in + * assembler, but a bitch in C... This is one + * of them. + */ +static u8 +adfs_dir_checkbyte(const struct adfs_dir *dir) +{ + struct buffer_head * const *bh = dir->bh; + const int blocksize_bits = dir->sb->s_blocksize_bits; + union { u32 *ptr32; u8 *ptr8; } ptr, end; + u32 dircheck = 0; + int last = 5 - 26; + int i = 0; + + /* + * Accumulate each word up to the last whole + * word of the last directory entry. This + * can spread across several buffer heads. + */ + do { + last += 26; + do { + dircheck = cpu_to_le32(dir_u32(i)) ^ ror13(dircheck); + + i += sizeof(u32); + } while (i < (last & ~3)); + } while (dir_u8(last) != 0); + + /* + * Accumulate the last few bytes. These + * bytes will be within the same bh. + */ + if (i != last) { + ptr.ptr8 = bufoff(bh, i); + end.ptr8 = ptr.ptr8 + last - i; + + do + dircheck = *ptr.ptr8++ ^ ror13(dircheck); + while (ptr.ptr8 < end.ptr8); + } + + /* + * The directory tail is in the final bh + * Note that contary to the RISC OS PRMs, + * the first few bytes are NOT included + * in the check. All bytes are in the + * same bh. + */ + ptr.ptr8 = bufoff(bh, 2008); + end.ptr8 = ptr.ptr8 + 36; + + do { + unsigned int v = *ptr.ptr32++; + dircheck = cpu_to_le32(v) ^ ror13(dircheck); + } while (ptr.ptr32 < end.ptr32); + + return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff; +} + +/* + * Read and check that a directory is valid + */ +int +adfs_dir_read(struct super_block *sb, unsigned long object_id, + unsigned int size, struct adfs_dir *dir) +{ + const unsigned int blocksize_bits = sb->s_blocksize_bits; + int blk = 0; + + /* + * Directories which are not a multiple of 2048 bytes + * are considered bad v2 [3.6] + */ + if (size & 2047) + goto bad_dir; + + size >>= blocksize_bits; + + dir->nr_buffers = 0; + dir->sb = sb; + + for (blk = 0; blk < size; blk++) { + int phys; + + phys = __adfs_block_map(sb, object_id, blk); + if (!phys) { + adfs_error(sb, "dir object %lX has a hole at offset %d", + object_id, blk); + goto release_buffers; + } + + dir->bh[blk] = bread(sb->s_dev, phys, sb->s_blocksize); + if (!dir->bh[blk]) + goto release_buffers; + } + + memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); + memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); + + if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || + memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) + goto bad_dir; + + if (memcmp(&dir->dirhead.startname, "Nick", 4) && + memcmp(&dir->dirhead.startname, "Hugo", 4)) + goto bad_dir; + + if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) + goto bad_dir; + + dir->nr_buffers = blk; + + return 0; + +bad_dir: + adfs_error(sb, "corrupted directory fragment %lX", + object_id); +release_buffers: + for (blk -= 1; blk >= 0; blk -= 1) + brelse(dir->bh[blk]); + + dir->sb = NULL; + + return -EIO; +} + +/* + * convert a disk-based directory entry to a Linux ADFS directory entry + */ +static inline void +adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de) +{ + obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN); + obj->file_id = adfs_readval(de->dirinddiscadd, 3); + obj->loadaddr = adfs_readval(de->dirload, 4); + obj->execaddr = adfs_readval(de->direxec, 4); + obj->size = adfs_readval(de->dirlen, 4); + obj->attr = de->newdiratts; +} + +/* + * convert a Linux ADFS directory entry to a disk-based directory entry + */ +static inline void +adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj) +{ + adfs_writeval(de->dirinddiscadd, 3, obj->file_id); + adfs_writeval(de->dirload, 4, obj->loadaddr); + adfs_writeval(de->direxec, 4, obj->execaddr); + adfs_writeval(de->dirlen, 4, obj->size); + de->newdiratts = obj->attr; +} + +/* + * get a directory entry. Note that the caller is responsible + * for holding the relevent locks. + */ +int +__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj) +{ + struct super_block *sb = dir->sb; + struct adfs_direntry de; + int thissize, buffer, offset; + + buffer = pos >> sb->s_blocksize_bits; + + if (buffer > dir->nr_buffers) + return -EINVAL; + + offset = pos & (sb->s_blocksize - 1); + thissize = sb->s_blocksize - offset; + if (thissize > 26) + thissize = 26; + + memcpy(&de, dir->bh[buffer]->b_data + offset, thissize); + if (thissize != 26) + memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data, + 26 - thissize); + + if (!de.dirobname[0]) + return -ENOENT; + + adfs_dir2obj(obj, &de); + + return 0; +} + +int +__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj) +{ + struct super_block *sb = dir->sb; + struct adfs_direntry de; + int thissize, buffer, offset; + + buffer = pos >> sb->s_blocksize_bits; + + if (buffer > dir->nr_buffers) + return -EINVAL; + + offset = pos & (sb->s_blocksize - 1); + thissize = sb->s_blocksize - offset; + if (thissize > 26) + thissize = 26; + + /* + * Get the entry in total + */ + memcpy(&de, dir->bh[buffer]->b_data + offset, thissize); + if (thissize != 26) + memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data, + 26 - thissize); + + /* + * update it + */ + adfs_obj2dir(&de, obj); + + /* + * Put the new entry back + */ + memcpy(dir->bh[buffer]->b_data + offset, &de, thissize); + if (thissize != 26) + memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize, + 26 - thissize); + + return 0; +} + +/* + * the caller is responsible for holding the necessary + * locks. + */ +static int +adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id) +{ + int pos, ret; + + ret = -ENOENT; + + for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) { + struct object_info obj; + + if (!__adfs_dir_get(dir, pos, &obj)) + break; + + if (obj.file_id == object_id) { + ret = pos; + break; + } + } + + return ret; +} + +static int +adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) +{ + int ret; + + if (sz != ADFS_NEWDIR_SIZE) + return -EIO; + + ret = adfs_dir_read(sb, id, sz, dir); + if (ret) + adfs_error(sb, "unable to read directory"); + else + dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3); + + return ret; +} + +static int +adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos) +{ + if (fpos >= ADFS_NUM_DIR_ENTRIES) + return -ENOENT; + + dir->pos = 5 + fpos * 26; + return 0; +} + +static int +adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj) +{ + unsigned int ret; + + ret = __adfs_dir_get(dir, dir->pos, obj); + if (ret == 0) + dir->pos += 26; + + return ret; +} + +static int +adfs_f_update(struct adfs_dir *dir, struct object_info *obj) +{ + struct super_block *sb = dir->sb; + int ret, i; + + ret = adfs_dir_find_entry(dir, obj->file_id); + if (ret < 0) { + adfs_error(dir->sb, "unable to locate entry to update"); + goto out; + } + + __adfs_dir_put(dir, ret, obj); + + /* + * Increment directory sequence number + */ + dir->bh[0]->b_data[0] += 1; + dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1; + + ret = adfs_dir_checkbyte(dir); + /* + * Update directory check byte + */ + dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret; + +#if 1 + { + const unsigned int blocksize_bits = sb->s_blocksize_bits; + + memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); + memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); + + if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || + memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) + goto bad_dir; + + if (memcmp(&dir->dirhead.startname, "Nick", 4) && + memcmp(&dir->dirhead.startname, "Hugo", 4)) + goto bad_dir; + + if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) + goto bad_dir; + } +#endif + for (i = dir->nr_buffers - 1; i >= 0; i--) + mark_buffer_dirty(dir->bh[i], 1); + + ret = 0; +out: + return ret; +#if 1 +bad_dir: + adfs_error(dir->sb, "whoops! I broke a directory!"); + return -EIO; +#endif +} + +static void +adfs_f_free(struct adfs_dir *dir) +{ + int i; + + for (i = dir->nr_buffers - 1; i >= 0; i--) { + brelse(dir->bh[i]); + dir->bh[i] = NULL; + } + + dir->nr_buffers = 0; + dir->sb = NULL; +} + +struct adfs_dir_ops adfs_f_dir_ops = { + adfs_f_read, + adfs_f_setpos, + adfs_f_getnext, + adfs_f_update, + NULL, + NULL, + adfs_f_free +}; diff --git a/fs/adfs/dir_f.h b/fs/adfs/dir_f.h new file mode 100644 index 000000000..8df158739 --- /dev/null +++ b/fs/adfs/dir_f.h @@ -0,0 +1,61 @@ +/* + * linux/fs/adfs/dir_f.h + * + * Copyright (C) 1999 Russell King + * + * Structures of directories on the F format disk + */ +#ifndef ADFS_DIR_F_H +#define ADFS_DIR_F_H + +/* + * Directory header + */ +struct adfs_dirheader { + unsigned char startmasseq; + unsigned char startname[4]; +}; + +#define ADFS_NEWDIR_SIZE 2048 +#define ADFS_NUM_DIR_ENTRIES 77 + +/* + * Directory entries + */ +struct adfs_direntry { +#define ADFS_F_NAME_LEN 10 + char dirobname[ADFS_F_NAME_LEN]; + __u8 dirload[4]; + __u8 direxec[4]; + __u8 dirlen[4]; + __u8 dirinddiscadd[3]; + __u8 newdiratts; +}; + +/* + * Directory tail + */ +union adfs_dirtail { + struct { + unsigned char dirlastmask; + char dirname[10]; + unsigned char dirparent[3]; + char dirtitle[19]; + unsigned char reserved[14]; + unsigned char endmasseq; + unsigned char endname[4]; + unsigned char dircheckbyte; + } old; + struct { + unsigned char dirlastmask; + unsigned char reserved[2]; + unsigned char dirparent[3]; + char dirtitle[19]; + char dirname[10]; + unsigned char endmasseq; + unsigned char endname[4]; + unsigned char dircheckbyte; + } new; +}; + +#endif diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c new file mode 100644 index 000000000..afb770d0d --- /dev/null +++ b/fs/adfs/dir_fplus.c @@ -0,0 +1,182 @@ +/* + * linux/fs/adfs/dir_fplus.c + * + * Copyright (C) 1997-1999 Russell King + */ +#include <linux/version.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/sched.h> +#include <linux/stat.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +#include <linux/spinlock.h> +#else +#include <asm/spinlock.h> +#endif + +#include "adfs.h" +#include "dir_fplus.h" + +static int +adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) +{ + struct adfs_bigdirheader *h; + struct adfs_bigdirtail *t; + unsigned long block; + unsigned int blk, size; + int i, ret = -EIO; + + dir->nr_buffers = 0; + + block = __adfs_block_map(sb, id, 0); + if (!block) { + adfs_error(sb, "dir object %X has a hole at offset 0", id); + goto out; + } + + dir->bh[0] = bread(sb->s_dev, block, sb->s_blocksize); + if (!dir->bh[0]) + goto out; + dir->nr_buffers += 1; + + h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + size = le32_to_cpu(h->bigdirsize); + if (size != sz) { + printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n" + " does not match directory size\n"); + } + + if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 || + h->bigdirversion[2] != 0 || size & 2047 || + h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) + goto out; + + size >>= sb->s_blocksize_bits; + for (blk = 1; blk < size; blk++) { + block = __adfs_block_map(sb, id, blk); + if (!block) { + adfs_error(sb, "dir object %X has a hole at offset %d", id, blk); + goto out; + } + + dir->bh[blk] = bread(sb->s_dev, block, sb->s_blocksize); + if (!dir->bh[blk]) + goto out; + dir->nr_buffers = blk; + } + + t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8)); + + if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || + t->bigdirendmasseq != h->startmasseq || + t->reserved[0] != 0 || t->reserved[1] != 0) + goto out; + + dir->parent_id = le32_to_cpu(h->bigdirparent); + dir->sb = sb; + return 0; +out: + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bh[i]); + dir->sb = NULL; + return ret; +} + +static int +adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) +{ + struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + int ret = -ENOENT; + + if (fpos <= le32_to_cpu(h->bigdirentries)) { + dir->pos = fpos; + ret = 0; + } + + return ret; +} + +static void +dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len) +{ + struct super_block *sb = dir->sb; + unsigned int buffer, partial, remainder; + + buffer = offset >> sb->s_blocksize_bits; + offset &= sb->s_blocksize - 1; + + partial = sb->s_blocksize - offset; + + if (partial >= len) + memcpy(to, dir->bh[buffer]->b_data + offset, len); + else { + char *c = (char *)to; + + remainder = len - partial; + + memcpy(c, dir->bh[buffer]->b_data + offset, partial); + memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder); + } +} + +static int +adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) +{ + struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + struct adfs_bigdirentry bde; + unsigned int offset; + int i, ret = -ENOENT; + + if (dir->pos >= le32_to_cpu(h->bigdirentries)) + goto out; + + offset = offsetof(struct adfs_bigdirheader, bigdirname); + offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); + offset += dir->pos * sizeof(struct adfs_bigdirentry); + + dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry)); + + obj->loadaddr = le32_to_cpu(bde.bigdirload); + obj->execaddr = le32_to_cpu(bde.bigdirexec); + obj->size = le32_to_cpu(bde.bigdirlen); + obj->file_id = le32_to_cpu(bde.bigdirindaddr); + obj->attr = le32_to_cpu(bde.bigdirattr); + obj->name_len = le32_to_cpu(bde.bigdirobnamelen); + + offset = offsetof(struct adfs_bigdirheader, bigdirname); + offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); + offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry); + offset += le32_to_cpu(bde.bigdirobnameptr); + + dir_memcpy(dir, offset, obj->name, obj->name_len); + for (i = 0; i < obj->name_len; i++) + if (obj->name[i] == '/') + obj->name[i] = '.'; + + dir->pos += 1; + ret = 0; +out: + return ret; +} + +static void +adfs_fplus_free(struct adfs_dir *dir) +{ + int i; + + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bh[i]); + dir->sb = NULL; +} + +struct adfs_dir_ops adfs_fplus_dir_ops = { + adfs_fplus_read, + adfs_fplus_setpos, + adfs_fplus_getnext, + NULL, + NULL, + NULL, + adfs_fplus_free +}; diff --git a/fs/adfs/dir_fplus.h b/fs/adfs/dir_fplus.h new file mode 100644 index 000000000..5b3adefca --- /dev/null +++ b/fs/adfs/dir_fplus.h @@ -0,0 +1,41 @@ +/* + * linux/fs/adfs/dir_fplus.h + * + * Copyright (C) 1999 Russell King + * + * Structures of directories on the F+ format disk + */ + +#define ADFS_FPLUS_NAME_LEN 255 + +#define BIGDIRSTARTNAME ('S' | 'B' << 8 | 'P' << 16 | 'r' << 24) +#define BIGDIRENDNAME ('o' | 'v' << 8 | 'e' << 16 | 'n' << 24) + +struct adfs_bigdirheader { + __u8 startmasseq; + __u8 bigdirversion[3]; + __u32 bigdirstartname; + __u32 bigdirnamelen; + __u32 bigdirsize; + __u32 bigdirentries; + __u32 bigdirnamesize; + __u32 bigdirparent; + char bigdirname[1]; +}; + +struct adfs_bigdirentry { + __u32 bigdirload; + __u32 bigdirexec; + __u32 bigdirlen; + __u32 bigdirindaddr; + __u32 bigdirattr; + __u32 bigdirobnamelen; + __u32 bigdirobnameptr; +}; + +struct adfs_bigdirtail { + __u32 bigdirendname; + __u8 bigdirendmasseq; + __u8 reserved[2]; + __u8 bigdircheckbyte; +}; diff --git a/fs/adfs/file.c b/fs/adfs/file.c index a4580426a..a2f105b06 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -1,7 +1,7 @@ /* * linux/fs/adfs/file.c * - * Copyright (C) 1997 Russell King + * Copyright (C) 1997-1999 Russell King * from: * * linux/fs/ext2/file.c @@ -19,7 +19,7 @@ * * adfs regular file handling primitives */ - +#include <linux/version.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/ext2_fs.h> @@ -27,42 +27,21 @@ #include <linux/sched.h> #include <linux/stat.h> +#include "adfs.h" + /* * We have mostly NULLs here: the current defaults are OK for * the adfs filesystem. */ static struct file_operations adfs_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - NULL, /* write */ - NULL, /* readdir - bad */ - NULL, /* select - default */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* open - not special */ - NULL, /* flush */ - NULL, /* release */ - file_fsync, /* fsync */ - NULL, /* fasync */ + read: generic_file_read, + mmap: generic_file_mmap, + fsync: file_fsync, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + write: generic_file_write, +#endif }; struct inode_operations adfs_file_inode_operations = { - &adfs_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - adfs_bmap, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ + &adfs_file_operations, /* default file operations */ }; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index b33044af3..30398d62f 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -1,9 +1,9 @@ /* * linux/fs/adfs/inode.c * - * Copyright (C) 1997 Russell King + * Copyright (C) 1997-1999 Russell King */ - +#include <linux/version.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/adfs_fs.h> @@ -13,210 +13,367 @@ #include <linux/locks.h> #include <linux/mm.h> +#include "adfs.h" + /* - * Old Inode numbers: - * bit 30 - 16 FragID of parent object - * bit 15 0 1 - * bit 14 - 0 FragID of object Offset into parent FragID - * - * New Inode numbers: - * Inode = Frag ID of parent (14) + Frag Offset (8) + (index into directory + 1)(8) + * Lookup/Create a block at offset 'block' into 'inode'. We currently do + * not support creation of new blocks, so we return -EIO for this case. */ -#define inode_frag(ino) ((ino) >> 8) -#define inode_idx(ino) ((ino) & 0xff) -#define inode_dirindex(idx) (((idx) & 0xff) * 26 - 21) - -#define frag_id(x) (((x) >> 8) & 0x7fff) -#define off(x) (((x) & 0xff) ? (((x) & 0xff) - 1) << sb->u.adfs_sb.s_dr->log2sharesize : 0) - -static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +int +adfs_get_block(struct inode *inode, long block, struct buffer_head *bh, int create) { - unsigned long max_frag_id; + if (block < 0) + goto abort_negative; + + if (!create) { + if (block >= inode->i_blocks) + goto abort_toobig; + + block = __adfs_block_map(inode->i_sb, inode->i_ino, block); + if (block) { + bh->b_dev = inode->i_dev; + bh->b_blocknr = block; + bh->b_state |= (1UL << BH_Mapped); + } + return 0; + } + /* don't support allocation of blocks yet */ + return -EIO; - max_frag_id = sb->u.adfs_sb.s_map_size * sb->u.adfs_sb.s_ids_per_zone; +abort_negative: + adfs_error(inode->i_sb, "block %d < 0", block); + return -EIO; - return (inode_no & 0x800000ff) || - (frag_id (inode_frag (inode_no)) > max_frag_id) || - (frag_id (inode_frag (inode_no)) < 2); +abort_toobig: + return 0; } - -int adfs_inode_validate (struct inode *inode) +static int adfs_writepage(struct dentry *dentry, struct page *page) { - struct super_block *sb = inode->i_sb; + return block_write_full_page(page,adfs_get_block); +} +static int adfs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,adfs_get_block); +} +static int adfs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,adfs_get_block, + &((struct inode*)page->mapping->host)->u.adfs_i.mmu_private); +} +static int _adfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,adfs_get_block); +} +struct address_space_operations adfs_aops = { + readpage: adfs_readpage, + writepage: adfs_writepage, + prepare_write: adfs_prepare_write, + commit_write: generic_commit_write, + bmap: _adfs_bmap +}; +#else +int adfs_bmap(struct inode *inode, int block) +{ + if (block >= inode->i_blocks) + return 0; - return adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00) || - adfs_inode_validate_no (sb, inode->u.adfs_i.file_id << 8); + return __adfs_block_map(inode->i_sb, inode->i_ino, block); } +#endif -unsigned long adfs_inode_generate (unsigned long parent_id, int diridx) +static inline unsigned int +adfs_filetype(struct inode *inode) { - if (!parent_id) - return -1; + unsigned int type; - if (diridx) - diridx = (diridx + 21) / 26; + if (inode->u.adfs_i.stamped) + type = (inode->u.adfs_i.loadaddr >> 8) & 0xfff; + else + type = (unsigned int) -1; - return (parent_id << 8) | diridx; + return type; } -unsigned long adfs_inode_objid (struct inode *inode) +/* + * Convert ADFS attributes and filetype to Linux permission. + */ +static umode_t +adfs_atts2mode(struct super_block *sb, struct inode *inode) { - if (adfs_inode_validate (inode)) { - adfs_error (inode->i_sb, "adfs_inode_objid", - "bad inode number: %lu (%X,%X)", - inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id); - return 0; + unsigned int filetype, attr = inode->u.adfs_i.attr; + umode_t mode, rmask; + + if (attr & ADFS_NDA_DIRECTORY) { + mode = S_IRUGO & sb->u.adfs_sb.s_owner_mask; + return S_IFDIR | S_IXUGO | mode; } - return inode->u.adfs_i.file_id; -} + filetype = adfs_filetype(inode); -int adfs_bmap (struct inode *inode, int block) -{ - struct super_block *sb = inode->i_sb; - unsigned int blk; + switch (filetype) { + case 0xfc0: /* LinkFS */ + return S_IFLNK|S_IRWXUGO; - if (adfs_inode_validate (inode)) { - adfs_error (sb, "adfs_bmap", - "bad inode number: %lu (%X,%X)", - inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id); - return 0; - } + case 0xfe6: /* UnixExec */ + rmask = S_IRUGO | S_IXUGO; + break; - if (block < 0) { - adfs_error(sb, "adfs_bmap", "block(%d) < 0", block); - return 0; + default: + rmask = S_IRUGO; } - if (block > inode->i_blocks) - return 0; + mode = S_IFREG; - block += off(inode->u.adfs_i.file_id); + if (attr & ADFS_NDA_OWNER_READ) + mode |= rmask & sb->u.adfs_sb.s_owner_mask; - if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG) - blk = sb->u.adfs_sb.s_map_block + block; - else - blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), block); - return blk; + if (attr & ADFS_NDA_OWNER_WRITE) + mode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask; + + if (attr & ADFS_NDA_PUBLIC_READ) + mode |= rmask & sb->u.adfs_sb.s_other_mask; + + if (attr & ADFS_NDA_PUBLIC_WRITE) + mode |= S_IWUGO & sb->u.adfs_sb.s_other_mask; + return mode; } -unsigned int adfs_parent_bmap (struct inode *inode, int block) +/* + * Convert Linux permission to ADFS attribute. We try to do the reverse + * of atts2mode, but there is not a 1:1 translation. + */ +static int +adfs_mode2atts(struct super_block *sb, struct inode *inode) { - struct super_block *sb = inode->i_sb; - unsigned int blk, fragment; + umode_t mode; + int attr; - if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) { - adfs_error (sb, "adfs_parent_bmap", - "bad inode number: %lu (%X,%X)", - inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id); - return 0; - } + /* FIXME: should we be able to alter a link? */ + if (S_ISLNK(inode->i_mode)) + return inode->u.adfs_i.attr; - fragment = inode_frag (inode->i_ino); - if (frag_id (fragment) == ADFS_ROOT_FRAG) - blk = sb->u.adfs_sb.s_map_block + off(fragment) + block; + if (S_ISDIR(inode->i_mode)) + attr = ADFS_NDA_DIRECTORY; else - blk = adfs_map_lookup (sb, frag_id (fragment), off(fragment) + block); - return blk; + attr = 0; + + mode = inode->i_mode & sb->u.adfs_sb.s_owner_mask; + if (mode & S_IRUGO) + attr |= ADFS_NDA_OWNER_READ; + if (mode & S_IWUGO) + attr |= ADFS_NDA_OWNER_WRITE; + + mode = inode->i_mode & sb->u.adfs_sb.s_other_mask; + mode &= ~sb->u.adfs_sb.s_owner_mask; + if (mode & S_IRUGO) + attr |= ADFS_NDA_PUBLIC_READ; + if (mode & S_IWUGO) + attr |= ADFS_NDA_PUBLIC_WRITE; + + return attr; } -static int adfs_atts2mode(struct super_block *sb, unsigned char mode, unsigned int filetype) +/* + * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time + * referenced to 1 Jan 1900 (til 2248) + */ +static unsigned int +adfs_adfs2unix_time(struct inode *inode) { - int omode = 0; - - if (filetype == 0xfc0 /* LinkFS */) { - omode = S_IFLNK|S_IRUSR|S_IWUSR|S_IXUSR| - S_IRGRP|S_IWGRP|S_IXGRP| - S_IROTH|S_IWOTH|S_IXOTH; - } else { - if (mode & ADFS_NDA_DIRECTORY) { - omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask; - omode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH; - } else - omode |= S_IFREG; - - if (mode & ADFS_NDA_OWNER_READ) { - omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask; - if (filetype == 0xfe6 /* UnixExec */) - omode |= S_IXUGO & sb->u.adfs_sb.s_owner_mask; - } + unsigned int high, low; - if (mode & ADFS_NDA_OWNER_WRITE) - omode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask; + if (inode->u.adfs_i.stamped == 0) + return CURRENT_TIME; - if (mode & ADFS_NDA_PUBLIC_READ) { - omode |= S_IRUGO & sb->u.adfs_sb.s_other_mask; - if (filetype == 0xfe6 /* UnixExec */) - omode |= S_IXUGO & sb->u.adfs_sb.s_other_mask; - } + high = inode->u.adfs_i.loadaddr << 24; + low = inode->u.adfs_i.execaddr; - if (mode & ADFS_NDA_PUBLIC_WRITE) - omode |= S_IWUGO & sb->u.adfs_sb.s_other_mask; - } - return omode; + high |= low >> 8; + low &= 255; + + /* Files dated pre 01 Jan 1970 00:00:00. */ + if (high < 0x336e996a) + return 0; + + /* Files dated post 18 Jan 2038 03:14:05. */ + if (high >= 0x656e9969) + return 0x7ffffffd; + + /* discard 2208988800 (0x336e996a00) seconds of time */ + high -= 0x336e996a; + + /* convert 40-bit centi-seconds to 32-bit seconds */ + return (((high % 100) << 8) + low) / 100 + (high / 100 << 8); } -void adfs_read_inode (struct inode *inode) +/* + * Convert an Unix time to ADFS time. We only do this if the entry has a + * time/date stamp already. + */ +static void +adfs_unix2adfs_time(struct inode *inode, unsigned int secs) { - struct super_block *sb; - struct buffer_head *bh[4]; - struct adfs_idir_entry ide; - int buffers; - - sb = inode->i_sb; - inode->i_uid = sb->u.adfs_sb.s_uid; - inode->i_gid = sb->u.adfs_sb.s_gid; - inode->i_version = ++event; + unsigned int high, low; + + if (inode->u.adfs_i.stamped) { + /* convert 32-bit seconds to 40-bit centi-seconds */ + low = (secs & 255) * 100; + high = (secs / 256) * 100 + (low << 8) + 0x336e996a; - if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) { - adfs_error (sb, "adfs_read_inode", - "bad inode number: %lu", inode->i_ino); - goto bad; + inode->u.adfs_i.loadaddr = (high >> 24) | + (inode->u.adfs_i.loadaddr & ~0xff); + inode->u.adfs_i.execaddr = (low & 255) | (high << 8); } +} - if (frag_id(inode_frag (inode->i_ino)) == ADFS_ROOT_FRAG && - inode_idx (inode->i_ino) == 0) { - /* root dir */ - inode->i_mode = S_IRWXUGO | S_IFDIR; - inode->i_nlink = 2; - inode->i_size = ADFS_NEWDIR_SIZE; - inode->i_blksize = PAGE_SIZE; - inode->i_blocks = inode->i_size >> sb->s_blocksize_bits; - inode->i_mtime = - inode->i_atime = - inode->i_ctime = 0; - inode->u.adfs_i.file_id = inode_frag (inode->i_ino); - } else { - if (!(buffers = adfs_dir_read_parent (inode, bh))) - goto bad; - - if (adfs_dir_check (inode, bh, buffers, NULL)) { - adfs_dir_free (bh, buffers); - goto bad; - } +/* + * Fill in the inode information from the object information. + * + * Note that this is an inode-less filesystem, so we can't use the inode + * number to reference the metadata on the media. Instead, we use the + * inode number to hold the object ID, which in turn will tell us where + * the data is held. We also save the parent object ID, and with these + * two, we can locate the metadata. + * + * This does mean that we rely on an objects parent remaining the same at + * all times - we cannot cope with a cross-directory rename (yet). + */ +struct inode * +adfs_iget(struct super_block *sb, struct object_info *obj) +{ + struct inode *inode; - if (!adfs_dir_find_entry (sb, bh, buffers, inode_dirindex (inode->i_ino), &ide)) { - adfs_dir_free (bh, buffers); - goto bad; - } - adfs_dir_free (bh, buffers); - inode->i_mode = adfs_atts2mode(sb, ide.mode, ide.filetype); - inode->i_nlink = 2; - inode->i_size = ide.size; - inode->i_blksize = PAGE_SIZE; - inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; - inode->i_mtime = - inode->i_atime = - inode->i_ctime = ide.mtime; - inode->u.adfs_i.file_id = ide.file_id; - } + inode = get_empty_inode(); + if (!inode) + goto out; + + inode->i_version = ++event; + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_uid = sb->u.adfs_sb.s_uid; + inode->i_gid = sb->u.adfs_sb.s_gid; + inode->i_ino = obj->file_id; + inode->i_size = obj->size; + inode->i_nlink = 2; + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> + sb->s_blocksize_bits; + + /* + * we need to save the parent directory ID so that + * write_inode can update the directory information + * for this file. This will need special handling + * for cross-directory renames. + */ + inode->u.adfs_i.parent_id = obj->parent_id; + inode->u.adfs_i.loadaddr = obj->loadaddr; + inode->u.adfs_i.execaddr = obj->execaddr; + inode->u.adfs_i.attr = obj->attr; + inode->u.adfs_i.stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000); + + inode->i_mode = adfs_atts2mode(sb, inode); + inode->i_mtime = + inode->i_atime = + inode->i_ctime = adfs_adfs2unix_time(inode); if (S_ISDIR(inode->i_mode)) - inode->i_op = &adfs_dir_inode_operations; - else if (S_ISREG(inode->i_mode)) + inode->i_op = &adfs_dir_inode_operations; + else if (S_ISREG(inode->i_mode)) { inode->i_op = &adfs_file_inode_operations; - return; + inode->i_mapping->a_ops = &adfs_aops; + inode->u.adfs_i.mmu_private = inode->i_size; + } + + insert_inode_hash(inode); -bad: +out: + return inode; +} + +/* + * This is no longer a valid way to obtain the metadata associated with the + * inode number on this filesystem. This means that this filesystem cannot + * be shared via NFS. + */ +void adfs_read_inode(struct inode *inode) +{ + adfs_error(inode->i_sb, "unsupported method of reading inode"); make_bad_inode(inode); } + +/* + * Validate and convert a changed access mode/time to their ADFS equivalents. + * adfs_write_inode will actually write the information back to the directory + * later. + */ +int +adfs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = inode->i_sb; + unsigned int ia_valid = attr->ia_valid; + int error; + + error = inode_change_ok(inode, attr); + + /* + * we can't change the UID or GID of any file - + * we have a global UID/GID in the superblock + */ + if ((ia_valid & ATTR_UID && attr->ia_uid != sb->u.adfs_sb.s_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != sb->u.adfs_sb.s_gid)) + error = -EPERM; + + if (error) + goto out; + + if (ia_valid & ATTR_SIZE) + inode->i_size = attr->ia_size; + if (ia_valid & ATTR_MTIME) { + inode->i_mtime = attr->ia_mtime; + adfs_unix2adfs_time(inode, attr->ia_mtime); + } + /* + * FIXME: should we make these == to i_mtime since we don't + * have the ability to represent them in our filesystem? + */ + if (ia_valid & ATTR_ATIME) + inode->i_atime = attr->ia_atime; + if (ia_valid & ATTR_CTIME) + inode->i_ctime = attr->ia_ctime; + if (ia_valid & ATTR_MODE) { + inode->u.adfs_i.attr = adfs_mode2atts(sb, inode); + inode->i_mode = adfs_atts2mode(sb, inode); + } + + /* + * FIXME: should we be marking this inode dirty even if + * we don't have any metadata to write back? + */ + if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE)) + mark_inode_dirty(inode); +out: + return error; +} + +/* + * write an existing inode back to the directory, and therefore the disk. + * The adfs-specific inode data has already been updated by + * adfs_notify_change() + */ +void adfs_write_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct object_info obj; + + obj.file_id = inode->i_ino; + obj.name_len = 0; + obj.parent_id = inode->u.adfs_i.parent_id; + obj.loadaddr = inode->u.adfs_i.loadaddr; + obj.execaddr = inode->u.adfs_i.execaddr; + obj.attr = inode->u.adfs_i.attr; + obj.size = inode->i_size; + + adfs_dir_update(sb, &obj); +} diff --git a/fs/adfs/map.c b/fs/adfs/map.c index 31d560143..0fde11f5d 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -1,108 +1,302 @@ /* * linux/fs/adfs/map.c * - * Copyright (C) 1997 Russell King + * Copyright (C) 1997-1999 Russell King */ - +#include <linux/version.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/adfs_fs.h> -static inline unsigned int -adfs_convert_map_to_sector (const struct super_block *sb, unsigned int mapoff) -{ - if (sb->u.adfs_sb.s_map2blk >= 0) - mapoff <<= sb->u.adfs_sb.s_map2blk; - else - mapoff >>= -sb->u.adfs_sb.s_map2blk; - return mapoff; -} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +#include <linux/spinlock.h> +#else +#include <asm/spinlock.h> +#endif + +#include "adfs.h" -static inline unsigned int -adfs_convert_sector_to_map (const struct super_block *sb, unsigned int secoff) +/* + * For the future... + */ +static rwlock_t adfs_map_lock; + +/* + * return the map bit offset of the fragment frag_id in + * the zone dm. + * Note that the loop is optimised for best asm code - + * look at the output of: + * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c + */ +static int +lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, + const unsigned int frag_id, unsigned int *offset) { - if (sb->u.adfs_sb.s_map2blk >= 0) - secoff >>= sb->u.adfs_sb.s_map2blk; - else - secoff <<= -sb->u.adfs_sb.s_map2blk; - return secoff; + const unsigned int mapsize = dm->dm_endbit; + const unsigned int idmask = (1 << idlen) - 1; + unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1; + unsigned int start = dm->dm_startbit; + unsigned int mapptr; + + do { + unsigned long frag; + + /* + * get fragment id + */ + //asm("@ get fragment id start"); + { + unsigned long v2; + unsigned int tmp; + + tmp = start >> 5; + + frag = le32_to_cpu(map[tmp]); + v2 = le32_to_cpu(map[tmp + 1]); + + tmp = start & 31; + + frag = (frag >> tmp) | (v2 << (32 - tmp)); + + frag &= idmask; + } + //asm("@ get fragment id end"); + + mapptr = start + idlen; + + /* + * find end of fragment + */ + //asm("@ find end of fragment start"); + { + unsigned long v2; + + while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) { + mapptr = (mapptr & ~31) + 32; + if (mapptr >= mapsize) + goto error; + } + + mapptr += 1 + ffz(~v2); + } + //asm("@ find end of fragment end"); + + if (frag == frag_id) + goto found; +again: + start = mapptr; + } while (mapptr < mapsize); + +error: + return -1; + +found: + { + int length = mapptr - start; + if (*offset >= length) { + *offset -= length; + goto again; + } + } + return start + *offset; } -static int lookup_zone (struct super_block *sb, int zone, int frag_id, int *offset) +/* + * Scan the free space map, for this zone, calculating the total + * number of map bits in each free space fragment. + * + * Note: idmask is limited to 15 bits [3.2] + */ +static unsigned int +scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) { - unsigned int mapptr, idlen, mapsize; - unsigned long *map; + const unsigned int mapsize = dm->dm_endbit + 32; + const unsigned int idlen = asb->s_idlen; + const unsigned int frag_idlen = idlen <= 15 ? idlen : 15; + const unsigned int idmask = (1 << frag_idlen) - 1; + unsigned long *map = (unsigned long *)dm->dm_bh->b_data; + unsigned int start = 8, mapptr; + unsigned long frag; + unsigned long total = 0; + + /* + * get fragment id + */ + //asm("@ get fragment id start"); + { + unsigned long v2; + unsigned int tmp; + + tmp = start >> 5; + + frag = le32_to_cpu(map[tmp]); + v2 = le32_to_cpu(map[tmp + 1]); + + tmp = start & 31; + + frag = (frag >> tmp) | (v2 << (32 - tmp)); + + frag &= idmask; + } + //asm("@ get fragment id end"); - map = ((unsigned long *)sb->u.adfs_sb.s_map[zone]->b_data) + 1; - zone = - mapptr = zone == 0 ? (ADFS_DR_SIZE << 3) : 0; - idlen = sb->u.adfs_sb.s_idlen; - mapsize = sb->u.adfs_sb.s_zonesize; + /* + * If the freelink is null, then no free fragments + * exist in this zone. + */ + if (frag == 0) + return 0; do { - unsigned long v1, v2; - unsigned int start; + start += frag; - v1 = map[mapptr>>5]; - v2 = map[(mapptr>>5)+1]; + /* + * get fragment id + */ + //asm("@ get fragment id start"); + { + unsigned long v2; + unsigned int tmp; - v1 = (v1 >> (mapptr & 31)) | (v2 << (32 - (mapptr & 31))); - start = mapptr; - mapptr += idlen; + tmp = start >> 5; + + frag = le32_to_cpu(map[tmp]); + v2 = le32_to_cpu(map[tmp + 1]); + + tmp = start & 31; + + frag = (frag >> tmp) | (v2 << (32 - tmp)); - v2 = map[mapptr >> 5] >> (mapptr & 31); - if (!v2) { - mapptr = (mapptr + 32) & ~31; - for (; (v2 = map[mapptr >> 5]) == 0 && mapptr < mapsize; mapptr += 32); + frag &= idmask; } - for (; (v2 & 255) == 0; v2 >>= 8, mapptr += 8); - for (; (v2 & 1) == 0; v2 >>= 1, mapptr += 1); - mapptr += 1; - - if ((v1 & ((1 << idlen) - 1)) == frag_id) { - int length = mapptr - start; - if (*offset >= length) - *offset -= length; - else - return start + *offset - zone; + //asm("@ get fragment id end"); + + mapptr = start + idlen; + + /* + * find end of fragment + */ + //asm("@ find end of fragment start"); + { + unsigned long v2; + + while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) { + mapptr = (mapptr & ~31) + 32; + if (mapptr >= mapsize) + goto error; + } + + mapptr += 1 + ffz(~v2); } - } while (mapptr < mapsize); + //asm("@ find end of fragment end"); + + total += mapptr - start; + } while (frag >= idlen + 1); + + if (frag != 0) + printk(KERN_ERR "adfs: undersized free fragment\n"); + + return total; +error: + printk(KERN_ERR "adfs: oversized free fragment\n"); + return 0; +} + +static int +scan_map(struct adfs_sb_info *asb, unsigned int zone, + const unsigned int frag_id, unsigned int mapoff) +{ + const unsigned int idlen = asb->s_idlen; + struct adfs_discmap *dm, *dm_end; + int result; + + dm = asb->s_map + zone; + zone = asb->s_map_size; + dm_end = asb->s_map + zone; + + do { + result = lookup_zone(dm, idlen, frag_id, &mapoff); + + if (result != -1) + goto found; + + dm ++; + if (dm == dm_end) + dm = asb->s_map; + } while (--zone > 0); + return -1; +found: + result -= dm->dm_startbit; + result += dm->dm_startblk; + + return result; +} + +/* + * calculate the amount of free blocks in the map. + * + * n=1 + * total_free = E(free_in_zone_n) + * nzones + */ +unsigned int +adfs_map_free(struct super_block *sb) +{ + struct adfs_sb_info *asb = &sb->u.adfs_sb; + struct adfs_discmap *dm; + unsigned int total = 0; + unsigned int zone; + + dm = asb->s_map; + zone = asb->s_map_size; + + do { + total += scan_free_map(asb, dm++); + } while (--zone > 0); + + return signed_asl(total, asb->s_map2blk); } int adfs_map_lookup (struct super_block *sb, int frag_id, int offset) { - unsigned int start_zone, zone, max_zone, mapoff, secoff; + struct adfs_sb_info *asb = &sb->u.adfs_sb; + unsigned int zone, mapoff; + int result; - zone = start_zone = frag_id / sb->u.adfs_sb.s_ids_per_zone; - max_zone = sb->u.adfs_sb.s_map_size; + /* + * map & root fragment is special - it starts in the center of the + * disk. The other fragments start at zone (frag / ids_per_zone) + */ + if (frag_id == ADFS_ROOT_FRAG) + zone = asb->s_map_size >> 1; + else + zone = frag_id / asb->s_ids_per_zone; - if (start_zone >= max_zone) { - adfs_error (sb, "adfs_map_lookup", "fragment %X is invalid (zone = %d, max = %d)", - frag_id, start_zone, max_zone); - return 0; - } + if (zone >= asb->s_map_size) + goto bad_fragment; /* Convert sector offset to map offset */ - mapoff = adfs_convert_sector_to_map (sb, offset); - /* Calculate sector offset into map block */ - secoff = offset - adfs_convert_map_to_sector (sb, mapoff); + mapoff = signed_asl(offset, -asb->s_map2blk); - do { - int result = lookup_zone (sb, zone, frag_id, &mapoff); + read_lock(&adfs_map_lock); + result = scan_map(asb, zone, frag_id, mapoff); + read_unlock(&adfs_map_lock); - if (result != -1) { - result += zone ? (zone * sb->u.adfs_sb.s_zonesize) - (ADFS_DR_SIZE << 3): 0; - return adfs_convert_map_to_sector (sb, result) + secoff; - } + if (result > 0) { + unsigned int secoff; - zone ++; - if (zone >= max_zone) - zone = 0; + /* Calculate sector offset into map block */ + secoff = offset - signed_asl(mapoff, asb->s_map2blk); + return secoff + signed_asl(result, asb->s_map2blk); + } - } while (zone != start_zone); + adfs_error(sb, "fragment %04X at offset %d not found in map", + frag_id, offset); + return 0; - adfs_error (sb, "adfs_map_lookup", "fragment %X at offset %d not found in map (start zone %d)", - frag_id, offset, start_zone); +bad_fragment: + adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)", + frag_id, zone, asb->s_map_size); return 0; } diff --git a/fs/adfs/namei.c b/fs/adfs/namei.c deleted file mode 100644 index 4e41c0975..000000000 --- a/fs/adfs/namei.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * linux/fs/adfs/namei.c - * - * Copyright (C) 1997 Russell King - */ - -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/adfs_fs.h> -#include <linux/fcntl.h> -#include <linux/sched.h> -#include <linux/stat.h> -#include <linux/string.h> -#include <linux/locks.h> - -/* - * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure - */ -static int adfs_match (int len, const char * const name, struct adfs_idir_entry *de) -{ - int i; - - if (!de || len > ADFS_NAME_LEN) - return 0; - /* - * "" means "." ---> so paths like "/usr/lib//libc.a" work - */ - if (!len && de->name_len == 1 && de->name[0] == '.' && - de->name[1] == '\0') - return 1; - if (len != de->name_len) - return 0; - - for (i = 0; i < len; i++) - if ((de->name[i] ^ name[i]) & 0x5f) - return 0; - return 1; -} - -static int adfs_find_entry (struct inode *dir, const char * const name, int namelen, - struct adfs_idir_entry *ide) -{ - struct super_block *sb; - struct buffer_head *bh[4]; - union adfs_dirtail dt; - unsigned long parent_object_id, dir_object_id; - int buffers, pos; - - sb = dir->i_sb; - - if (adfs_inode_validate (dir)) { - adfs_error (sb, "adfs_find_entry", - "invalid inode number: %lu", dir->i_ino); - return 0; - } - - if (!(buffers = adfs_dir_read (dir, bh))) { - adfs_error (sb, "adfs_find_entry", "unable to read directory"); - return 0; - } - - if (adfs_dir_check (dir, bh, buffers, &dt)) { - adfs_dir_free (bh, buffers); - return 0; - } - - parent_object_id = adfs_val (dt.new.dirparent, 3); - dir_object_id = adfs_inode_objid (dir); - - if (namelen == 2 && name[0] == '.' && name[1] == '.') { - ide->name_len = 2; - ide->name[0] = ide->name[1] = '.'; - ide->name[2] = '\0'; - ide->inode_no = adfs_inode_generate (parent_object_id, 0); - adfs_dir_free (bh, buffers); - return 1; - } - - pos = 5; - - do { - if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, ide)) - break; - - if (adfs_match (namelen, name, ide)) { - adfs_dir_free (bh, buffers); - return pos; - } - pos += 26; - } while (1); - adfs_dir_free (bh, buffers); - return 0; -} - -struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = NULL; - struct adfs_idir_entry de; - unsigned long ino; - - if (dentry->d_name.len > ADFS_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); - - if (adfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de)) { - ino = de.inode_no; - inode = iget (dir->i_sb, ino); - - if (!inode) - return ERR_PTR(-EACCES); - } - d_add(dentry, inode); - return NULL; -} diff --git a/fs/adfs/super.c b/fs/adfs/super.c index c8db4d2e1..7b7eb2d0e 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -1,9 +1,9 @@ /* * linux/fs/adfs/super.c * - * Copyright (C) 1997 Russell King + * Copyright (C) 1997-1999 Russell King */ - +#include <linux/version.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/fs.h> @@ -21,12 +21,11 @@ #include <stdarg.h> -static void adfs_put_super(struct super_block *sb); -static int adfs_remount(struct super_block *sb, int *flags, char *data); -static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); -void adfs_read_inode(struct inode *inode); +#include "adfs.h" +#include "dir_f.h" +#include "dir_fplus.h" -void adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) +void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) { char error_buf[128]; va_list args; @@ -40,7 +39,74 @@ void adfs_error(struct super_block *sb, const char *function, const char *fmt, . function ? function : "", error_buf); } -static unsigned char adfs_calczonecheck(struct super_block *sb, char *map) +static int adfs_checkdiscrecord(struct adfs_discrecord *dr) +{ + int i; + + /* sector size must be 256, 512 or 1024 bytes */ + if (dr->log2secsize != 8 && + dr->log2secsize != 9 && + dr->log2secsize != 10) + return 1; + + /* idlen must be at least log2secsize + 3 */ + if (dr->idlen < dr->log2secsize + 3) + return 1; + + /* we cannot have such a large disc that we + * are unable to represent sector offsets in + * 32 bits. This works out at 2.0 TB. + */ + if (dr->disc_size_high >> dr->log2secsize) + return 1; + + /* + * The following checks are not required for F+ + * stage 1. + */ +#if 0 + /* idlen must be smaller be no greater than 15 */ + if (dr->idlen > 15) + return 1; + + /* nzones must be less than 128 for the root + * directory to be addressable + */ + if (dr->nzones >= 128 && dr->nzones_high == 0) + return 1; + + /* root must be of the form 0x2.. */ + if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200) + return 1; +#else + /* + * Stage 2 F+ does not require the following check + */ +#if 0 + /* idlen must be no greater than 16 v2 [1.0] */ + if (dr->idlen > 16) + return 1; + + /* we can't handle F+ discs yet */ + if (dr->format_version || dr->root_size) + return 1; + +#else + /* idlen must be no greater than 19 v2 [1.0] */ + if (dr->idlen > 19) + return 1; +#endif +#endif + + /* reserved bytes should be zero */ + for (i = 0; i < sizeof(dr->unused52); i++) + if (dr->unused52[i] != 0) + return 1; + + return 0; +} + +static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map) { unsigned int v0, v1, v2, v3; int i; @@ -64,46 +130,34 @@ static unsigned char adfs_calczonecheck(struct super_block *sb, char *map) return v0 ^ v1 ^ v2 ^ v3; } -static int adfs_checkmap(struct super_block *sb) +static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm) { unsigned char crosscheck = 0, zonecheck = 1; int i; for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) { - char *map; + unsigned char *map; + + map = dm[i].dm_bh->b_data; - map = sb->u.adfs_sb.s_map[i]->b_data; if (adfs_calczonecheck(sb, map) != map[0]) { - adfs_error(sb, "adfs_checkmap", "zone %d fails zonecheck", i); + adfs_error(sb, "zone %d fails zonecheck", i); zonecheck = 0; } crosscheck ^= map[3]; } if (crosscheck != 0xff) - adfs_error(sb, "adfs_checkmap", "crosscheck != 0xff"); + adfs_error(sb, "crosscheck != 0xff"); return crosscheck == 0xff && zonecheck; } -static struct super_operations adfs_sops = { - adfs_read_inode, - NULL, - NULL, - NULL, - NULL, - adfs_put_super, - NULL, - adfs_statfs, - adfs_remount -}; - static void adfs_put_super(struct super_block *sb) { int i; for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse(sb->u.adfs_sb.s_map[i]); + brelse(sb->u.adfs_sb.s_map[i].dm_bh); kfree(sb->u.adfs_sb.s_map); - brelse(sb->u.adfs_sb.s_sbh); MOD_DEC_USE_COUNT; } @@ -159,13 +213,107 @@ static int adfs_remount(struct super_block *sb, int *flags, char *data) return parse_options(sb, data); } +static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +{ + struct adfs_sb_info *asb = &sb->u.adfs_sb; + struct statfs tmp; + + tmp.f_type = ADFS_SUPER_MAGIC; + tmp.f_namelen = asb->s_namelen; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = asb->s_size; + tmp.f_files = asb->s_ids_per_zone * asb->s_map_size; + tmp.f_bavail = + tmp.f_bfree = adfs_map_free(sb); + tmp.f_ffree = tmp.f_bfree * tmp.f_files / tmp.f_blocks; + + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +static struct super_operations adfs_sops = { + adfs_read_inode, /* read_inode */ + adfs_write_inode, /* write_inode */ + NULL, /* put_inode */ + NULL, /* delete_inode */ + adfs_notify_change, /* notify_change */ + adfs_put_super, /* put_super */ + NULL, /* write_super */ + adfs_statfs, /* statfs */ + adfs_remount, /* remount_fs */ + NULL, /* clear_inode */ + NULL /* umount_begin */ +}; + +static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr) +{ + struct adfs_discmap *dm; + unsigned int map_addr, zone_size, nzones; + int i, zone; + + nzones = sb->u.adfs_sb.s_map_size; + zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare); + map_addr = (nzones >> 1) * zone_size - + ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0); + map_addr = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk); + + sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1); + + dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL); + if (dm == NULL) { + adfs_error(sb, "not enough memory"); + return NULL; + } + + for (zone = 0; zone < nzones; zone++, map_addr++) { + dm[zone].dm_startbit = 0; + dm[zone].dm_endbit = zone_size; + dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS; + dm[zone].dm_bh = bread(sb->s_dev, map_addr, sb->s_blocksize); + + if (!dm[zone].dm_bh) { + adfs_error(sb, "unable to read map"); + goto error_free; + } + } + + /* adjust the limits for the first and last map zones */ + i = zone - 1; + dm[0].dm_startblk = 0; + dm[0].dm_startbit = ADFS_DR_SIZE_BITS; + dm[i].dm_endbit = (dr->disc_size_high << (32 - dr->log2bpmb)) + + (dr->disc_size >> dr->log2bpmb) + + (ADFS_DR_SIZE_BITS - i * zone_size); + + if (adfs_checkmap(sb, dm)) + return dm; + + adfs_error(sb, NULL, "map corrupted"); + +error_free: + while (--zone >= 0) + brelse(dm[zone].dm_bh); + + kfree(dm); + return NULL; +} + +static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits) +{ + unsigned long discsize; + + discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits); + discsize |= le32_to_cpu(dr->disc_size) >> block_bits; + + return discsize; +} + struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent) { struct adfs_discrecord *dr; struct buffer_head *bh; + struct object_info root_obj; unsigned char *b_data; kdev_t dev = sb->s_dev; - int i, j; /* set default options */ sb->u.adfs_sb.s_uid = 0; @@ -180,7 +328,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile lock_super(sb); set_blocksize(dev, BLOCK_SIZE); if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) { - adfs_error(sb, NULL, "unable to read superblock"); + adfs_error(sb, "unable to read superblock"); goto error_unlock; } @@ -192,8 +340,19 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile "%s.\n", kdevname(dev)); goto error_free_bh; } + dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); + /* + * Do some sanity checks on the ADFS disc record + */ + if (adfs_checkdiscrecord(dr)) { + if (!silent) + printk("VPS: Can't find an adfs filesystem on dev " + "%s.\n", kdevname(dev)); + goto error_free_bh; + } + sb->s_blocksize_bits = dr->log2secsize; sb->s_blocksize = 1 << sb->s_blocksize_bits; if (sb->s_blocksize != BLOCK_SIZE && @@ -204,13 +363,13 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile set_blocksize(dev, sb->s_blocksize); bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize); if (!bh) { - adfs_error(sb, NULL, "couldn't read superblock on " + adfs_error(sb, "couldn't read superblock on " "2nd try."); goto error_unlock; } b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); if (adfs_checkbblk(b_data)) { - adfs_error(sb, NULL, "disc record mismatch, very weird!"); + adfs_error(sb, "disc record mismatch, very weird!"); goto error_free_bh; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); @@ -221,92 +380,66 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile "%s.\n", kdevname(dev)); goto error_free_bh; } - /* blocksize on this device should now be set to the adfs log2secsize */ - sb->u.adfs_sb.s_sbh = bh; - sb->u.adfs_sb.s_dr = dr; - - /* s_zone_size = size of 1 zone (1 sector) * bits_in_byte - zone_spare => - * number of map bits in a zone - */ - sb->u.adfs_sb.s_zone_size = (8 << dr->log2secsize) - dr->zone_spare; - - /* s_ids_per_zone = bit size of 1 zone / min. length of fragment block => - * number of ids in one zone + /* + * blocksize on this device should now be set to the ADFS log2secsize */ - sb->u.adfs_sb.s_ids_per_zone = sb->u.adfs_sb.s_zone_size / (dr->idlen + 1); - - /* s_idlen = length of 1 id */ - sb->u.adfs_sb.s_idlen = dr->idlen; - - /* map size (in sectors) = number of zones */ - sb->u.adfs_sb.s_map_size = dr->nzones; - - /* zonesize = size of sector - zonespare */ - sb->u.adfs_sb.s_zonesize = (sb->s_blocksize << 3) - dr->zone_spare; - /* map start (in sectors) = start of zone (number of zones) / 2 */ - sb->u.adfs_sb.s_map_block = (dr->nzones >> 1) * sb->u.adfs_sb.s_zone_size - - ((dr->nzones > 1) ? 8 * ADFS_DR_SIZE : 0); + sb->s_magic = ADFS_SUPER_MAGIC; + sb->u.adfs_sb.s_idlen = dr->idlen; + sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8); + sb->u.adfs_sb.s_map2blk = dr->log2bpmb - dr->log2secsize; + sb->u.adfs_sb.s_size = adfs_discsize(dr, sb->s_blocksize_bits); + sb->u.adfs_sb.s_version = dr->format_version; + sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize; - /* (signed) number of bits to shift left a map address to a sector address */ - sb->u.adfs_sb.s_map2blk = dr->log2bpmb - dr->log2secsize; - - if (sb->u.adfs_sb.s_map2blk >= 0) - sb->u.adfs_sb.s_map_block <<= sb->u.adfs_sb.s_map2blk; - else - sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk; - - printk(KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n", - sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone, - sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size); - printk(KERN_DEBUG "ADFS: sector size %d, map bit size %d, share size %d\n", - 1 << dr->log2secsize, 1 << dr->log2bpmb, - 1 << (dr->log2secsize + dr->log2sharesize)); - - sb->s_magic = ADFS_SUPER_MAGIC; - - sb->u.adfs_sb.s_map = kmalloc(sb->u.adfs_sb.s_map_size * - sizeof(struct buffer_head *), GFP_KERNEL); - if (sb->u.adfs_sb.s_map == NULL) { - adfs_error(sb, NULL, "not enough memory"); + sb->u.adfs_sb.s_map = adfs_read_map(sb, dr); + if (!sb->u.adfs_sb.s_map) goto error_free_bh; - } - for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) { - sb->u.adfs_sb.s_map[i] = bread(dev, - sb->u.adfs_sb.s_map_block + i, - sb->s_blocksize); - if (!sb->u.adfs_sb.s_map[i]) { - for (j = 0; j < i; j++) - brelse(sb->u.adfs_sb.s_map[j]); - kfree(sb->u.adfs_sb.s_map); - adfs_error(sb, NULL, "unable to read map"); - goto error_free_bh; - } - } - if (!adfs_checkmap(sb)) { - for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse(sb->u.adfs_sb.s_map[i]); - adfs_error(sb, NULL, "map corrupted"); - goto error_free_bh; - } + brelse(bh); - dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4); + /* + * set up enough so that we can read an inode + */ + sb->s_op = &adfs_sops; unlock_super(sb); + dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4); + + root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root); + root_obj.name_len = 0; + root_obj.loadaddr = 0; + root_obj.execaddr = 0; + root_obj.size = ADFS_NEWDIR_SIZE; + root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ | + ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; + /* - * set up enough so that it can read an inode + * If this is a F+ disk with variable length directories, + * get the root_size from the disc record. */ - sb->s_op = &adfs_sops; - sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0); - sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root)); + if (sb->u.adfs_sb.s_version) { + root_obj.size = dr->root_size; + sb->u.adfs_sb.s_dir = &adfs_fplus_dir_ops; + sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN; + } else { + sb->u.adfs_sb.s_dir = &adfs_f_dir_ops; + sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj)); +#else + sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj), NULL); +#endif if (!sb->s_root) { + int i; + for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse(sb->u.adfs_sb.s_map[i]); - brelse(bh); - adfs_error(sb, NULL, "get root inode failed\n"); + brelse(sb->u.adfs_sb.s_map[i].dm_bh); + kfree(sb->u.adfs_sb.s_map); + adfs_error(sb, "get root inode failed\n"); goto error_dec_use; } return sb; @@ -322,65 +455,6 @@ error: return NULL; } -static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) -{ - struct statfs tmp; - const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1; - - tmp.f_type = ADFS_SUPER_MAGIC; - tmp.f_bsize = sb->s_blocksize; - tmp.f_blocks = sb->u.adfs_sb.s_dr->disc_size_high << (32 - sb->s_blocksize_bits) | - sb->u.adfs_sb.s_dr->disc_size >> sb->s_blocksize_bits; - tmp.f_files = tmp.f_blocks >> nidlen; - { - unsigned int i, j = 0; - const unsigned mask = (1 << (nidlen - 1)) - 1; - for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) { - const char *map = sb->u.adfs_sb.s_map[i]->b_data; - unsigned freelink, mapindex = 24; - j -= nidlen; - do { - unsigned char k, l, m; - unsigned off = (mapindex - nidlen) >> 3; - unsigned rem; - const unsigned boff = mapindex & 7; - - /* get next freelink */ - - k = map[off++]; - l = map[off++]; - m = map[off++]; - freelink = (m << 16) | (l << 8) | k; - rem = freelink >> (boff + nidlen - 1); - freelink = (freelink >> boff) & mask; - mapindex += freelink; - - /* find its length and add it to running total */ - - while (rem == 0) { - j += 8; - rem = map[off++]; - } - if ((rem & 0xff) == 0) j+=8, rem>>=8; - if ((rem & 0xf) == 0) j+=4, rem>>=4; - if ((rem & 0x3) == 0) j+=2, rem>>=2; - if ((rem & 0x1) == 0) j+=1; - j += nidlen - boff; - if (freelink <= nidlen) break; - } while (mapindex < 8 * sb->s_blocksize); - if (mapindex > 8 * sb->s_blocksize) - adfs_error(sb, NULL, "oversized free fragment\n"); - else if (freelink) - adfs_error(sb, NULL, "undersized free fragment\n"); - } - tmp.f_bfree = tmp.f_bavail = j << - (sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits); - } - tmp.f_ffree = tmp.f_bfree >> nidlen; - tmp.f_namelen = ADFS_NAME_LEN; - return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; -} - static struct file_system_type adfs_fs_type = { "adfs", FS_REQUIRES_DEV, adfs_read_super, NULL }; diff --git a/fs/affs/dir.c b/fs/affs/dir.c index f126dcbb8..b554daf0c 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -28,17 +28,9 @@ static int affs_readdir(struct file *, void *, filldir_t); static ssize_t affs_dir_read(struct file *, char *, size_t, loff_t *); static struct file_operations affs_dir_operations = { - NULL, /* lseek - default */ - affs_dir_read, /* read */ - NULL, /* write - bad */ - affs_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync /* default fsync */ + read: affs_dir_read, + readdir: affs_readdir, + fsync: file_fsync, }; /* @@ -55,14 +47,6 @@ struct inode_operations affs_dir_inode_operations = { affs_rmdir, /* rmdir */ NULL, /* mknod */ affs_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permissions */ - NULL /* revalidate */ }; static ssize_t diff --git a/fs/affs/file.c b/fs/affs/file.c index 7a751e40b..8881fe3e4 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -44,19 +44,10 @@ static ssize_t affs_file_write_ofs(struct file *filp, const char *buf, size_t cn static int alloc_ext_cache(struct inode *inode); static struct file_operations affs_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - affs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open */ - NULL, /* flush */ - NULL, /* release */ - file_fsync, /* brute force, but works */ - NULL, /* fasync */ - NULL /* lock */ + read: generic_file_read, + write: affs_file_write, + mmap: generic_file_mmap, + fsync: file_fsync, }; struct inode_operations affs_file_inode_operations = { @@ -72,28 +63,15 @@ struct inode_operations affs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - affs_bmap, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ affs_truncate, /* truncate */ NULL, /* permission */ NULL /* revalidate */ }; static struct file_operations affs_file_operations_ofs = { - NULL, /* lseek - default */ - affs_file_read_ofs, /* read */ - affs_file_write_ofs, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open */ - NULL, /* flush */ - NULL, /* release */ - file_fsync, /* brute force, but works */ - NULL, /* fasync */ - NULL /* lock */ + read: affs_file_read_ofs, + write: affs_file_write_ofs, + fsync: file_fsync, }; struct inode_operations affs_file_inode_operations_ofs = { @@ -109,9 +87,6 @@ struct inode_operations affs_file_inode_operations_ofs = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ affs_truncate, /* truncate */ NULL, /* permission */ NULL /* revalidate */ @@ -341,6 +316,37 @@ affs_bmap(struct inode *inode, int block) return key; } +/* AFFS is currently broken */ +static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh, int create) +{ + BUG(); + return -1; +} +static int affs_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,affs_get_block); +} +static int affs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,affs_get_block); +} +static int affs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,affs_get_block, + &((struct inode*)page->mapping->host)->u.affs_i.mmu_private); +} +static int _affs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,affs_get_block); +} +struct address_space_operations affs_aops = { + readpage: affs_readpage, + writepage: affs_writepage, + prepare_write: affs_prepare_write, + commit_write: generic_commit_write, + bmap: _affs_bmap +}; + /* With the affs, getting a random block from a file is not * a simple business. Since this fs does not allow holes, * it may be necessary to allocate all the missing blocks diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 9b05ec062..ed4993e36 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -166,16 +166,20 @@ affs_read_inode(struct inode *inode) if (S_ISREG(inode->i_mode)) { if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) { inode->i_op = &affs_file_inode_operations_ofs; - } else { - inode->i_op = &affs_file_inode_operations; + return; } + inode->i_op = &affs_file_inode_operations; + inode->i_mapping->a_ops = &affs_aops; + inode->u.affs_i.mmu_private = inode->i_size; } else if (S_ISDIR(inode->i_mode)) { /* Maybe it should be controlled by mount parameter? */ inode->i_mode |= S_ISVTX; inode->i_op = &affs_dir_inode_operations; } - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &affs_symlink_inode_operations; + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &affs_symlink_aops; + } } void diff --git a/fs/affs/namei.c b/fs/affs/namei.c index aad5b8f14..d2c27b9d1 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -275,9 +275,11 @@ affs_create(struct inode *dir, struct dentry *dentry, int mode) pr_debug("AFFS: ino=%lu\n",inode->i_ino); if (dir->i_sb->u.affs_sb.s_flags & SF_OFS) inode->i_op = &affs_file_inode_operations_ofs; - else + else { inode->i_op = &affs_file_inode_operations; - + inode->i_mapping->a_ops = &affs_aops; + inode->u.affs_i.mmu_private = inode->i_size; + } error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE); if (error) goto out_iput; @@ -401,7 +403,8 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) if (!inode) goto out; - inode->i_op = &affs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &affs_symlink_aops; inode->i_mode = S_IFLNK | 0777; inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode); error = -EIO; diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index 6aad1c222..885d5099d 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -71,8 +71,6 @@ fail: return err; } -struct inode_operations affs_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, +struct address_space_operations affs_symlink_aops = { readpage: affs_symlink_readpage, }; diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index ae53396af..a62fd6c54 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -43,30 +43,12 @@ static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) } static struct file_operations autofs_dir_operations = { - NULL, /* llseek */ - NULL, /* read */ - NULL, /* write */ - autofs_dir_readdir, /* readdir */ + readdir: autofs_dir_readdir, }; struct inode_operations autofs_dir_inode_operations = { &autofs_dir_operations, /* file operations */ NULL, /* create */ autofs_dir_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/autofs/init.c b/fs/autofs/init.c index 5c31dc889..4c9e8fe76 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -31,8 +31,8 @@ static void __exit exit_autofs_fs(void) unregister_filesystem(&autofs_fs_type); } -module_init(init_autofs_fs) -module_exit(exit_autofs_fs) +module_init(init_autofs_fs); +module_exit(exit_autofs_fs); #ifdef DEBUG void autofs_say(const char *name, int len) diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 46fc4503e..e2c03ff34 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -24,12 +24,8 @@ static int autofs_root_mkdir(struct inode *,struct dentry *,int); static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); static struct file_operations autofs_root_operations = { - NULL, /* llseek */ - NULL, /* read */ - NULL, /* write */ - autofs_root_readdir, /* readdir */ - NULL, /* poll */ - autofs_root_ioctl, /* ioctl */ + readdir: autofs_root_readdir, + ioctl: autofs_root_ioctl, }; struct inode_operations autofs_root_inode_operations = { @@ -41,16 +37,6 @@ struct inode_operations autofs_root_inode_operations = { autofs_root_symlink, /* symlink */ autofs_root_mkdir, /* mkdir */ autofs_root_rmdir, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index e8487efd6..102f7213e 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -12,7 +12,7 @@ /* Internal header file for autofs */ -#include <linux/auto_fs.h> +#include <linux/auto_fs4.h> #include <linux/list.h> /* This is the range of ioctl() numbers we claim as ours */ diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 1b512ee1c..9c8bd133f 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -140,7 +140,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, *gid = current->gid; *pgrp = current->pgrp; - *minproto = *maxproto = AUTOFS_MAX_PROTO_VERSION; + *minproto = AUTOFS_MIN_PROTO_VERSION; + *maxproto = AUTOFS_MAX_PROTO_VERSION; *pipefd = -1; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 464c940b8..b3b45fc0b 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -86,7 +86,7 @@ static int autofs4_write(struct file *file, const void *addr, int bytes) static void autofs4_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq, - enum autofs_packet_type type) + int type) { union autofs_packet_union pkt; size_t pktsz; diff --git a/fs/bad_inode.c b/fs/bad_inode.c index bff2689a0..931a32a4e 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -30,19 +30,19 @@ static int return_EIO(void) static struct file_operations bad_file_ops = { - EIO_ERROR, /* lseek */ - EIO_ERROR, /* read */ - EIO_ERROR, /* write */ - EIO_ERROR, /* readdir */ - EIO_ERROR, /* select */ - EIO_ERROR, /* ioctl */ - EIO_ERROR, /* mmap */ - EIO_ERROR, /* open */ - EIO_ERROR, /* flush */ - EIO_ERROR, /* release */ - EIO_ERROR, /* fsync */ - EIO_ERROR, /* fasync */ - EIO_ERROR, /* lock */ + llseek: EIO_ERROR, + read: EIO_ERROR, + write: EIO_ERROR, + readdir: EIO_ERROR, + poll: EIO_ERROR, + ioctl: EIO_ERROR, + mmap: EIO_ERROR, + open: EIO_ERROR, + flush: EIO_ERROR, + release: EIO_ERROR, + fsync: EIO_ERROR, + fasync: EIO_ERROR, + lock: EIO_ERROR, }; struct inode_operations bad_inode_ops = @@ -59,9 +59,6 @@ struct inode_operations bad_inode_ops = EIO_ERROR, /* rename */ EIO_ERROR, /* readlink */ bad_follow_link, /* follow_link */ - EIO_ERROR, /* get_block */ - EIO_ERROR, /* readpage */ - EIO_ERROR, /* writepage */ EIO_ERROR, /* truncate */ EIO_ERROR, /* permission */ EIO_ERROR /* revalidate */ diff --git a/fs/bfs/bfs_defs.h b/fs/bfs/bfs_defs.h index 54d7dad29..c2756cdb9 100644 --- a/fs/bfs/bfs_defs.h +++ b/fs/bfs/bfs_defs.h @@ -5,7 +5,6 @@ #define su_lf_ioff u.bfs_sb.si_lf_ioff #define su_lf_sblk u.bfs_sb.si_lf_sblk #define su_lf_eblk u.bfs_sb.si_lf_eblk -#define su_bmap u.bfs_sb.si_bmap #define su_imap u.bfs_sb.si_imap #define su_sbh u.bfs_sb.si_sbh #define su_bfs_sb u.bfs_sb.si_bfs_sb @@ -13,3 +12,6 @@ #define iu_dsk_ino u.bfs_i.i_dsk_ino #define iu_sblock u.bfs_i.i_sblock #define iu_eblock u.bfs_i.i_eblock + +#define printf(format, args...) \ + printk(KERN_ERR "BFS-fs: " __FUNCTION__ "(): " format, ## args) diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index b6159be85..83b3e517b 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -14,9 +14,9 @@ #undef DEBUG #ifdef DEBUG -#define DBG(x...) printk(x) +#define dprintf(x...) printf(x) #else -#define DBG(x...) +#define dprintf(x...) #endif static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino); @@ -38,14 +38,13 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) int block; if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - printk(KERN_ERR "BFS-fs: %s(): Bad inode or not a directory %s:%08lx\n", - __FUNCTION__, bdevname(dev), dir->i_ino); + printf("Bad inode or not a directory %s:%08lx\n", bdevname(dev), dir->i_ino); return -EBADF; } if (f->f_pos & (BFS_DIRENT_SIZE-1)) { - printk(KERN_ERR "BFS-fs: %s(): Bad f_pos=%08lx for %s:%08lx\n", - __FUNCTION__, (unsigned long)f->f_pos, bdevname(dev), dir->i_ino); + printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos, + bdevname(dev), dir->i_ino); return -EBADF; } @@ -77,18 +76,9 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) } static struct file_operations bfs_dir_operations = { - llseek: NULL, read: bfs_dir_read, - write: NULL, readdir: bfs_readdir, - poll: NULL, - ioctl: NULL, - mmap: NULL, - open: NULL, - flush: NULL, - release: NULL, fsync: file_fsync, - fasync: NULL, }; extern void dump_imap(const char *, struct super_block *); @@ -117,6 +107,7 @@ static int bfs_create(struct inode * dir, struct dentry * dentry, int mode) inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_blocks = inode->i_blksize = 0; inode->i_op = &bfs_file_inops; + inode->i_mapping->a_ops = &bfs_aops; inode->i_mode = mode; inode->i_ino = inode->iu_dsk_ino = ino; inode->iu_sblock = inode->iu_eblock = 0; @@ -189,9 +180,8 @@ static int bfs_unlink(struct inode * dir, struct dentry * dentry) goto out_brelse; if (!inode->i_nlink) { - printk(KERN_WARNING - "BFS-fs: %s(): unlinking non-existent file %s:%lu (nlink=%d)\n", - __FUNCTION__, bdevname(inode->i_dev), inode->i_ino, inode->i_nlink); + printf("unlinking non-existent file %s:%lu (nlink=%d)\n", bdevname(inode->i_dev), + inode->i_ino, inode->i_nlink); inode->i_nlink = 1; } de->ino = 0; @@ -276,14 +266,6 @@ struct inode_operations bfs_dir_inops = { rmdir: NULL, mknod: NULL, rename: bfs_rename, - readlink: NULL, - follow_link: NULL, - get_block: NULL, - readpage: NULL, - writepage: NULL, - truncate: NULL, - permission: NULL, - revalidate: NULL }; static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino) @@ -294,7 +276,7 @@ static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int kdev_t dev; int i; - DBG(KERN_ERR "BFS-fs: %s(%s,%d)\n", __FUNCTION__, name, namelen); + dprintf("name=%s, namelen=%d\n", name, namelen); if (!namelen) return -ENOENT; diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 9e7503626..c3d5a8905 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -5,68 +5,149 @@ */ #include <linux/fs.h> +#include <linux/locks.h> #include <linux/bfs_fs.h> +#include <linux/smp_lock.h> #include "bfs_defs.h" #undef DEBUG #ifdef DEBUG -#define DBG(x...) printk(x) +#define dprintf(x...) printf(x) #else -#define DBG(x...) +#define dprintf(x...) #endif -static ssize_t bfs_file_write(struct file * f, const char * buf, size_t count, loff_t *ppos) -{ - return generic_file_write(f, buf, count, ppos, block_write_partial_page); -} - static struct file_operations bfs_file_operations = { - llseek: NULL, read: generic_file_read, - write: bfs_file_write, - readdir: NULL, - poll: NULL, - ioctl: NULL, + write: generic_file_write, mmap: generic_file_mmap, - open: NULL, - flush: NULL, - release: NULL, - fsync: NULL, - fasync: NULL, }; +static int bfs_move_block(unsigned long from, unsigned long to, kdev_t dev) +{ + struct buffer_head *bh, *new = NULL; + + bh = bread(dev, from, BFS_BSIZE); + if (!bh) + return -EIO; + new = getblk(dev, to, BFS_BSIZE); + memcpy(new->b_data, bh->b_data, bh->b_size); + mark_buffer_dirty(new, 1); + bforget(bh); + brelse(new); + return 0; +} + +static int bfs_move_blocks(kdev_t dev, unsigned long start, unsigned long end, + unsigned long where) +{ + unsigned long i; + + dprintf("%08lx-%08lx->%08lx\n", start, end, where); + for (i = start; i <= end; i++) + if(bfs_move_block(i, where + i, dev)) { + dprintf("failed to move block %08lx -> %08lx\n", i, where + i); + return -EIO; + } + return 0; +} + static int bfs_get_block(struct inode * inode, long block, struct buffer_head * bh_result, int create) { - long phys = inode->iu_sblock + block; - if (!create || phys <= inode->iu_eblock) { + long phys, next_free_block; + int err; + struct super_block *s = inode->i_sb; + + if (block < 0 || block > s->su_blocks) + return -EIO; + + phys = inode->iu_sblock + block; + if (!create) { + if (phys <= inode->iu_eblock) { + dprintf("c=%d, b=%08lx, phys=%08lx (granted)\n", create, block, phys); + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } + return 0; + } + + /* if the file is not empty and the requested block is within the range + of blocks allocated for this file, we can grant it */ + if (inode->i_size && phys <= inode->iu_eblock) { + dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", create, block, phys); bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = phys; bh_result->b_state |= (1UL << BH_Mapped); return 0; - } - /* no support for file migration, working on it */ - return -EIO; + } + + /* the rest has to be protected against itself */ + lock_kernel(); + + /* if the last data block for this file is the last allocated block, we can + extend the file trivially, without moving it anywhere */ + if (inode->iu_eblock == s->su_lf_eblk) { + dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", create, block, phys); + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + s->su_lf_eblk = inode->iu_eblock = inode->iu_sblock + block; + mark_inode_dirty(inode); + mark_buffer_dirty(s->su_sbh, 1); + err = 0; + goto out; + } + + /* Ok, we have to move this entire file to the next free block */ + next_free_block = s->su_lf_eblk + 1; + if (inode->iu_sblock) { /* if data starts on block 0 then there is no data */ + err = bfs_move_blocks(inode->i_dev, inode->iu_sblock, inode->iu_eblock, next_free_block); + if (err) { + dprintf("failed to move ino=%08lx -> possible fs corruption\n", inode->i_ino); + goto out; + } + } else + err = 0; + + inode->iu_sblock = next_free_block; + s->su_lf_eblk = inode->iu_eblock = next_free_block + block; + mark_inode_dirty(inode); + mark_buffer_dirty(s->su_sbh, 1); + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = inode->iu_sblock + block; + bh_result->b_state |= (1UL << BH_Mapped); +out: + unlock_kernel(); + return err; +} + +static int bfs_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,bfs_get_block); +} +static int bfs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,bfs_get_block); +} +static int bfs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,bfs_get_block); +} +static int bfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,bfs_get_block); } +struct address_space_operations bfs_aops = { + readpage: bfs_readpage, + writepage: bfs_writepage, + prepare_write: bfs_prepare_write, + commit_write: generic_commit_write, + bmap: bfs_bmap +}; struct inode_operations bfs_file_inops = { default_file_ops: &bfs_file_operations, - create: NULL, - lookup: NULL, - link: NULL, - unlink: NULL, - symlink: NULL, - mkdir: NULL, - rmdir: NULL, - mknod: NULL, - rename: NULL, - readlink: NULL, - follow_link: NULL, - get_block: bfs_get_block, - readpage: block_read_full_page, - writepage: block_write_full_page, - truncate: NULL, - permission: NULL, - revalidate: NULL }; diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index c0ee15dc4..14e440f27 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -5,7 +5,6 @@ * From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds. */ -#include <linux/config.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/slab.h> @@ -18,15 +17,15 @@ #include "bfs_defs.h" MODULE_AUTHOR("Tigran A. Aivazian"); -MODULE_DESCRIPTION("UnixWare BFS filesystem for Linux"); +MODULE_DESCRIPTION("SCO UnixWare BFS filesystem for Linux"); EXPORT_NO_SYMBOLS; #undef DEBUG #ifdef DEBUG -#define DBG(x...) printk(x) +#define dprintf(x...) printf(x) #else -#define DBG(x...) +#define dprintf(x...) #endif void dump_imap(const char *prefix, struct super_block * s); @@ -40,8 +39,7 @@ static void bfs_read_inode(struct inode * inode) int block, off; if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) { - printk(KERN_ERR "BFS-fs: %s(): Bad inode number %s:%08lx\n", - __FUNCTION__, bdevname(dev), ino); + printf("Bad inode number %s:%08lx\n", bdevname(dev), ino); make_bad_inode(inode); return; } @@ -49,8 +47,7 @@ static void bfs_read_inode(struct inode * inode) block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; bh = bread(dev, block, BFS_BSIZE); if (!bh) { - printk(KERN_ERR "BFS-fs: %s(): Unable to read inode %s:%08lx\n", - __FUNCTION__, bdevname(dev), ino); + printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino); make_bad_inode(inode); return; } @@ -65,6 +62,7 @@ static void bfs_read_inode(struct inode * inode) } else if (di->i_vtype == BFS_VREG) { inode->i_mode |= S_IFREG; inode->i_op = &bfs_file_inops; + inode->i_mapping->a_ops = &bfs_aops; } else inode->i_op = NULL; @@ -94,16 +92,14 @@ static void bfs_write_inode(struct inode * inode) int block, off; if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) { - printk(KERN_ERR "BFS-fs: %s(): Bad inode number %s:%08lx\n", - __FUNCTION__, bdevname(dev), ino); + printf("Bad inode number %s:%08lx\n", bdevname(dev), ino); return; } block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; bh = bread(dev, block, BFS_BSIZE); if (!bh) { - printk(KERN_ERR "BFS-fs: %s(): Unable to read inode %s:%08lx\n", - __FUNCTION__, bdevname(dev), ino); + printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino); return; } @@ -140,30 +136,12 @@ static void bfs_delete_inode(struct inode * inode) int block, off; struct super_block * s = inode->i_sb; - DBG(KERN_ERR "%s(ino=%08lx)\n", __FUNCTION__, inode->i_ino); + dprintf("ino=%08lx\n", inode->i_ino); - if (!inode) + if (!inode || !inode->i_dev || inode->i_count > 1 || inode->i_nlink || !s) return; - if (!inode->i_dev) { - printk(KERN_ERR "BFS-fs: free_inode(%08lx) !dev\n", inode->i_ino); - return; - } - if (inode->i_count > 1) { - printk(KERN_ERR "BFS-fs: free_inode(%08lx) count=%d\n", - inode->i_ino, inode->i_count); - return; - } - if (inode->i_nlink) { - printk(KERN_ERR "BFS-fs: free_inode(%08lx) nlink=%d\n", - inode->i_ino, inode->i_nlink); - return; - } - if (!inode->i_sb) { - printk(KERN_ERR "BFS-fs: free_inode(%08lx) !sb\n", inode->i_ino); - return; - } if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > inode->i_sb->su_lasti) { - printk(KERN_ERR "BFS-fs: free_inode(%08lx) invalid ino\n", inode->i_ino); + printf("invalid ino=%08lx\n", inode->i_ino); return; } @@ -173,8 +151,7 @@ static void bfs_delete_inode(struct inode * inode) block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; bh = bread(dev, block, BFS_BSIZE); if (!bh) { - printk(KERN_ERR "BFS-fs: %s(): Unable to read inode %s:%08lx\n", - __FUNCTION__, bdevname(dev), ino); + printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino); return; } off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; @@ -189,6 +166,14 @@ static void bfs_delete_inode(struct inode * inode) di->i_sblock = 0; mark_buffer_dirty(bh, 1); brelse(bh); + + /* if this was the last file, make the previous + block "last files last block" even if there is no real file there, + saves us 1 gap */ + if (s->su_lf_eblk == inode->iu_eblock) { + s->su_lf_eblk = inode->iu_sblock - 1; + mark_buffer_dirty(s->su_sbh, 1); + } clear_inode(inode); } @@ -196,7 +181,6 @@ static void bfs_put_super(struct super_block *s) { brelse(s->su_sbh); kfree(s->su_imap); - kfree(s->su_bmap); MOD_DEC_USE_COUNT; } @@ -251,7 +235,7 @@ void dump_imap(const char *prefix, struct super_block * s) else strcat(tmpbuf, "0"); } - printk(KERN_ERR "BFS-fs: %s: lasti=%d <%s>\n", prefix, s->su_lasti, tmpbuf); + printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, s->su_lasti, tmpbuf); free_page((unsigned long)tmpbuf); #endif } @@ -263,7 +247,7 @@ static struct super_block * bfs_read_super(struct super_block * s, struct buffer_head * bh; struct bfs_super_block * bfs_sb; struct inode * inode; - int i, imap_len, bmap_len; + int i, imap_len; MOD_INC_USE_COUNT; lock_super(s); @@ -272,58 +256,43 @@ static struct super_block * bfs_read_super(struct super_block * s, s->s_blocksize = BFS_BSIZE; s->s_blocksize_bits = BFS_BSIZE_BITS; - /* read ahead 8K to get inodes as we'll need them in a tick */ - bh = breada(dev, 0, BFS_BSIZE, 0, 8192); + bh = bread(dev, 0, BFS_BSIZE); if(!bh) goto out; bfs_sb = (struct bfs_super_block *)bh->b_data; if (bfs_sb->s_magic != BFS_MAGIC) { if (!silent) - printk(KERN_ERR "BFS-fs: No BFS filesystem on %s (magic=%08x)\n", - bdevname(dev), bfs_sb->s_magic); + printf("No BFS filesystem on %s (magic=%08x)\n", + bdevname(dev), bfs_sb->s_magic); goto out; } if (BFS_UNCLEAN(bfs_sb, s) && !silent) - printk(KERN_WARNING "BFS-fs: %s is unclean\n", bdevname(dev)); + printf("%s is unclean, continuing\n", bdevname(dev)); -#ifndef CONFIG_BFS_FS_WRITE - s->s_flags |= MS_RDONLY; -#endif s->s_magic = BFS_MAGIC; s->su_bfs_sb = bfs_sb; s->su_sbh = bh; s->su_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) + BFS_ROOT_INO - 1; - bmap_len = sizeof(struct bfs_bmap) * s->su_lasti; - s->su_bmap = kmalloc(bmap_len, GFP_KERNEL); - if (!s->su_bmap) - goto out; - memset(s->su_bmap, 0, bmap_len); imap_len = s->su_lasti/8 + 1; s->su_imap = kmalloc(imap_len, GFP_KERNEL); - if (!s->su_imap) { - kfree(s->su_bmap); + if (!s->su_imap) goto out; - } memset(s->su_imap, 0, imap_len); - for (i=0; i<BFS_ROOT_INO; i++) { - s->su_bmap[i].start = s->su_bmap[i].end = 0; + for (i=0; i<BFS_ROOT_INO; i++) set_bit(i, s->su_imap); - } s->s_op = &bfs_sops; inode = iget(s, BFS_ROOT_INO); if (!inode) { kfree(s->su_imap); - kfree(s->su_bmap); goto out; } s->s_root = d_alloc_root(inode); if (!s->s_root) { iput(inode); kfree(s->su_imap); - kfree(s->su_bmap); goto out; } @@ -335,10 +304,9 @@ static struct super_block * bfs_read_super(struct super_block * s, s->su_lf_ioff = 0; for (i=BFS_ROOT_INO; i<=s->su_lasti; i++) { inode = iget(s,i); - if (inode->iu_dsk_ino == 0) { + if (inode->iu_dsk_ino == 0) s->su_freei++; - s->su_bmap[i].start = s->su_bmap[i].end = 0; - } else { + else { set_bit(i, s->su_imap); s->su_freeb -= inode->i_blocks; if (inode->iu_eblock > s->su_lf_eblk) { @@ -346,8 +314,6 @@ static struct super_block * bfs_read_super(struct super_block * s, s->su_lf_sblk = inode->iu_sblock; s->su_lf_ioff = BFS_INO2OFF(i); } - s->su_bmap[i].start = inode->iu_sblock; - s->su_bmap[i].end = inode->iu_eblock; } iput(inode); } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 0d8a3c109..f4937eb91 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -405,7 +405,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) int elf_exec_fileno; int retval, size, i; unsigned long elf_entry, interp_load_addr = 0; - unsigned long start_code, end_code, end_data; + unsigned long start_code, end_code, start_data, end_data; struct elfhdr elf_ex; struct elfhdr interp_elf_ex; struct exec interp_ex; @@ -470,6 +470,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) start_code = ~0UL; end_code = 0; + start_data = 0; end_data = 0; for (i = 0; i < elf_ex.e_phnum; i++) { @@ -586,6 +587,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) goto out_free_dentry; /* OK, This is the point of no return */ + current->mm->start_data = 0; current->mm->end_data = 0; current->mm->end_code = 0; current->mm->mmap = NULL; @@ -650,7 +652,10 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) } k = elf_ppnt->p_vaddr; if (k < start_code) start_code = k; + if (start_data < k) start_data = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; + if (k > elf_bss) elf_bss = k; if ((elf_ppnt->p_flags & PF_X) && end_code < k) @@ -669,6 +674,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) elf_brk += load_bias; start_code += load_bias; end_code += load_bias; + start_data += load_bias; end_data += load_bias; if (elf_interpreter) { @@ -726,6 +732,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) current->mm->start_brk = current->mm->brk = elf_brk; current->mm->end_code = end_code; current->mm->start_code = start_code; + current->mm->start_data = start_data; current->mm->end_data = end_data; current->mm->start_stack = bprm->p; @@ -740,6 +747,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) printk("(start_brk) %lx\n" , (long) current->mm->start_brk); printk("(end_code) %lx\n" , (long) current->mm->end_code); printk("(start_code) %lx\n" , (long) current->mm->start_code); + printk("(start_data) %lx\n" , (long) current->mm->start_data); printk("(end_data) %lx\n" , (long) current->mm->end_data); printk("(start_stack) %lx\n" , (long) current->mm->start_stack); printk("(brk) %lx\n" , (long) current->mm->brk); diff --git a/fs/block_dev.c b/fs/block_dev.c index 47362da04..b451332ed 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -70,39 +70,53 @@ ssize_t block_write(struct file * filp, const char * buf, if (chars != blocksize) fn = bread; bh = fn(dev, block, blocksize); + if (!bh) + return written ? written : -EIO; + if (!buffer_uptodate(bh)) + wait_on_buffer(bh); } #else bh = getblk(dev, block, blocksize); + if (!bh) + return written ? written : -EIO; - if (chars != blocksize && !buffer_uptodate(bh)) { - if(!filp->f_reada || - !read_ahead[MAJOR(dev)]) { - /* We do this to force the read of a single buffer */ - brelse(bh); - bh = bread(dev,block,blocksize); - } else { - /* Read-ahead before write */ - blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2; - if (block + blocks > size) blocks = size - block; - if (blocks > NBUF) blocks=NBUF; + if (!buffer_uptodate(bh)) + { + if (chars == blocksize) + wait_on_buffer(bh); + else + { bhlist[0] = bh; - for(i=1; i<blocks; i++){ - bhlist[i] = getblk (dev, block+i, blocksize); - if(!bhlist[i]){ - while(i >= 0) brelse(bhlist[i--]); - return written ? written : -EIO; - }; - }; + if (!filp->f_reada || !read_ahead[MAJOR(dev)]) { + /* We do this to force the read of a single buffer */ + blocks = 1; + } else { + /* Read-ahead before write */ + blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2; + if (block + blocks > size) blocks = size - block; + if (blocks > NBUF) blocks=NBUF; + if (!blocks) blocks = 1; + for(i=1; i<blocks; i++) + { + bhlist[i] = getblk (dev, block+i, blocksize); + if (!bhlist[i]) + { + while(i >= 0) brelse(bhlist[i--]); + return written ? written : -EIO; + } + } + } ll_rw_block(READ, blocks, bhlist); for(i=1; i<blocks; i++) brelse(bhlist[i]); wait_on_buffer(bh); - + if (!buffer_uptodate(bh)) { + brelse(bh); + return written ? written : -EIO; + } }; }; #endif block++; - if (!bh) - return written ? written : -EIO; p = offset + bh->b_data; offset = 0; *ppos += chars; @@ -522,7 +536,7 @@ int check_disk_change(kdev_t dev) if (sb && invalidate_inodes(sb)) printk("VFS: busy inodes on changed media.\n"); - invalidate_buffers(dev); + destroy_buffers(dev); if (bdops->revalidate) bdops->revalidate(dev); diff --git a/fs/buffer.c b/fs/buffer.c index b2bc7670f..9113c07cc 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -94,6 +94,7 @@ static struct bh_free_head free_list[NR_SIZES]; kmem_cache_t *bh_cachep; static int grow_buffers(int size); +static void __refile_buffer(struct buffer_head *); /* This is used by some architectures to estimate available memory. */ atomic_t buffermem_pages = ATOMIC_INIT(0); @@ -277,11 +278,14 @@ repeat: void sync_dev(kdev_t dev) { - sync_buffers(dev, 0); sync_supers(dev); sync_inodes(dev); - sync_buffers(dev, 0); DQUOT_SYNC(dev); + /* sync all the dirty buffers out to disk only _after_ all the + high level layers finished generated buffer dirty data + (or we'll return with some buffer still dirty on the blockdevice + so breaking the semantics of this call) */ + sync_buffers(dev, 0); /* * FIXME(eric) we need to sync the physical devices here. * This is because some (scsi) controllers have huge amounts of @@ -323,7 +327,9 @@ int file_fsync(struct file *filp, struct dentry *dentry) struct inode * inode = dentry->d_inode; struct super_block * sb; kdev_t dev; + int ret; + lock_kernel(); /* sync the inode to buffers */ write_inode_now(inode); @@ -335,7 +341,9 @@ int file_fsync(struct file *filp, struct dentry *dentry) /* .. finally sync the buffers to disk */ dev = inode->i_dev; - return sync_buffers(dev, 1); + ret = sync_buffers(dev, 1); + unlock_kernel(); + return ret; } asmlinkage long sys_fsync(unsigned int fd) @@ -345,7 +353,6 @@ asmlinkage long sys_fsync(unsigned int fd) struct inode * inode; int err; - lock_kernel(); err = -EBADF; file = fget(fd); if (!file) @@ -371,7 +378,6 @@ asmlinkage long sys_fsync(unsigned int fd) out_putf: fput(file); out: - unlock_kernel(); return err; } @@ -382,7 +388,6 @@ asmlinkage long sys_fdatasync(unsigned int fd) struct inode * inode; int err; - lock_kernel(); err = -EBADF; file = fget(fd); if (!file) @@ -408,44 +413,9 @@ asmlinkage long sys_fdatasync(unsigned int fd) out_putf: fput(file); out: - unlock_kernel(); return err; } -void invalidate_buffers(kdev_t dev) -{ - int nlist; - - spin_lock(&lru_list_lock); - for(nlist = 0; nlist < NR_LIST; nlist++) { - struct buffer_head * bh; - int i; - retry: - bh = lru_list[nlist]; - if (!bh) - continue; - for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bh->b_next_free) { - if (bh->b_dev != dev) - continue; - if (buffer_locked(bh)) { - atomic_inc(&bh->b_count); - spin_unlock(&lru_list_lock); - wait_on_buffer(bh); - spin_lock(&lru_list_lock); - atomic_dec(&bh->b_count); - goto retry; - } - if (atomic_read(&bh->b_count)) - continue; - clear_bit(BH_Protected, &bh->b_state); - clear_bit(BH_Uptodate, &bh->b_state); - clear_bit(BH_Dirty, &bh->b_state); - clear_bit(BH_Req, &bh->b_state); - } - } - spin_unlock(&lru_list_lock); -} - /* After several hours of tedious analysis, the following hash * function won. Do not mess with it... -DaveM */ @@ -464,10 +434,12 @@ static __inline__ void __hash_link(struct buffer_head *bh, struct buffer_head ** static __inline__ void __hash_unlink(struct buffer_head *bh) { - if (bh->b_next) - bh->b_next->b_pprev = bh->b_pprev; - *(bh->b_pprev) = bh->b_next; - bh->b_pprev = NULL; + if (bh->b_pprev) { + if (bh->b_next) + bh->b_next->b_pprev = bh->b_pprev; + *(bh->b_pprev) = bh->b_next; + bh->b_pprev = NULL; + } } static void __insert_into_lru_list(struct buffer_head * bh, int blist) @@ -514,17 +486,12 @@ static void __remove_from_free_list(struct buffer_head * bh, int index) bh->b_next_free = bh->b_prev_free = NULL; } -/* The following two functions must operate atomically - * because they control the visibility of a buffer head - * to the rest of the kernel. - */ -static __inline__ void __remove_from_queues(struct buffer_head *bh) +/* must be called with both the hash_table_lock and the lru_list_lock + held */ +static void __remove_from_queues(struct buffer_head *bh) { - write_lock(&hash_table_lock); - if (bh->b_pprev) - __hash_unlink(bh); + __hash_unlink(bh); __remove_from_lru_list(bh, bh->b_list); - write_unlock(&hash_table_lock); } static void insert_into_queues(struct buffer_head *bh) @@ -547,6 +514,8 @@ static void put_last_free(struct buffer_head * bh) struct bh_free_head *head = &free_list[BUFSIZE_INDEX(bh->b_size)]; struct buffer_head **bhp = &head->list; + bh->b_state = 0; + spin_lock(&head->lock); bh->b_dev = B_FREE; if(!*bhp) { @@ -604,11 +573,73 @@ unsigned int get_hardblocksize(kdev_t dev) return 0; } +/* If invalidate_buffers() will trash dirty buffers, it means some kind + of fs corruption is going on. Trashing dirty data always imply losing + information that was supposed to be just stored on the physical layer + by the user. + + Thus invalidate_buffers in general usage is not allwowed to trash dirty + buffers. For example ioctl(FLSBLKBUF) expects dirty data to be preserved. + + NOTE: In the case where the user removed a removable-media-disk even if + there's still dirty data not synced on disk (due a bug in the device driver + or due an error of the user), by not destroying the dirty buffers we could + generate corruption also on the next media inserted, thus a parameter is + necessary to handle this case in the most safe way possible (trying + to not corrupt also the new disk inserted with the data belonging to + the old now corrupted disk). Also for the ramdisk the natural thing + to do in order to release the ramdisk memory is to destroy dirty buffers. + + These are two special cases. Normal usage imply the device driver + to issue a sync on the device (without waiting I/O completation) and + then an invalidate_buffers call that doesn't trashes dirty buffers. */ +void __invalidate_buffers(kdev_t dev, int destroy_dirty_buffers) +{ + int i, nlist, slept; + struct buffer_head * bh, * bh_next; + + retry: + slept = 0; + spin_lock(&lru_list_lock); + for(nlist = 0; nlist < NR_LIST; nlist++) { + bh = lru_list[nlist]; + if (!bh) + continue; + for (i = nr_buffers_type[nlist]; i > 0 ; bh = bh_next, i--) { + bh_next = bh->b_next_free; + if (bh->b_dev != dev) + continue; + if (buffer_locked(bh)) { + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + wait_on_buffer(bh); + slept = 1; + spin_lock(&lru_list_lock); + atomic_dec(&bh->b_count); + } + + write_lock(&hash_table_lock); + if (!atomic_read(&bh->b_count) && + (destroy_dirty_buffers || !buffer_dirty(bh))) { + __remove_from_queues(bh); + put_last_free(bh); + } + write_unlock(&hash_table_lock); + if (slept) + goto out; + } + } +out: + spin_unlock(&lru_list_lock); + if (slept) + goto retry; +} + void set_blocksize(kdev_t dev, int size) { extern int *blksize_size[]; - int i, nlist; - struct buffer_head * bh, *bhnext; + int i, nlist, slept; + struct buffer_head * bh, * bh_next; if (!blksize_size[MAJOR(dev)]) return; @@ -626,41 +657,53 @@ void set_blocksize(kdev_t dev, int size) sync_buffers(dev, 2); blksize_size[MAJOR(dev)][MINOR(dev)] = size; - /* We need to be quite careful how we do this - we are moving entries - * around on the free list, and we can get in a loop if we are not careful. - */ + retry: + slept = 0; + spin_lock(&lru_list_lock); for(nlist = 0; nlist < NR_LIST; nlist++) { - repeat: - spin_lock(&lru_list_lock); bh = lru_list[nlist]; - for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bhnext) { - if(!bh) - break; - - bhnext = bh->b_next_free; - if (bh->b_dev != dev) - continue; - if (bh->b_size == size) - continue; + if (!bh) + continue; + for (i = nr_buffers_type[nlist]; i > 0 ; bh = bh_next, i--) { + bh_next = bh->b_next_free; + if (bh->b_dev != dev || bh->b_size == size) + continue; if (buffer_locked(bh)) { atomic_inc(&bh->b_count); spin_unlock(&lru_list_lock); wait_on_buffer(bh); + slept = 1; + spin_lock(&lru_list_lock); atomic_dec(&bh->b_count); - goto repeat; - } - if (bh->b_dev == dev && bh->b_size != size) { - clear_bit(BH_Dirty, &bh->b_state); - clear_bit(BH_Uptodate, &bh->b_state); - clear_bit(BH_Req, &bh->b_state); } - if (atomic_read(&bh->b_count) == 0) { + + write_lock(&hash_table_lock); + if (!atomic_read(&bh->b_count)) { + if (buffer_dirty(bh)) + printk(KERN_WARNING + "set_blocksize: dev %s buffer_dirty %lu size %hu\n", + kdevname(dev), bh->b_blocknr, bh->b_size); __remove_from_queues(bh); put_last_free(bh); + } else { + if (atomic_set_buffer_clean(bh)) + __refile_buffer(bh); + clear_bit(BH_Uptodate, &bh->b_state); + printk(KERN_WARNING + "set_blocksize: " + "b_count %d, dev %s, block %lu, from %p\n", + atomic_read(&bh->b_count), bdevname(bh->b_dev), + bh->b_blocknr, __builtin_return_address(0)); } + write_unlock(&hash_table_lock); + if (slept) + goto out; } - spin_unlock(&lru_list_lock); } + out: + spin_unlock(&lru_list_lock); + if (slept) + goto retry; } /* @@ -785,30 +828,31 @@ repeat: atomic_set(&bh->b_count, 1); } spin_unlock(&free_list[isize].lock); - if (!bh) - goto refill; - /* OK, FINALLY we know that this buffer is the only one of its kind, - * we hold a reference (b_count>0), it is unlocked, and it is clean. + /* + * OK, FINALLY we know that this buffer is the only one of + * its kind, we hold a reference (b_count>0), it is unlocked, + * and it is clean. */ - init_buffer(bh, end_buffer_io_sync, NULL); - bh->b_dev = dev; - bh->b_blocknr = block; - bh->b_state = 1 << BH_Mapped; + if (bh) { + init_buffer(bh, end_buffer_io_sync, NULL); + bh->b_dev = dev; + bh->b_blocknr = block; + bh->b_state = 1 << BH_Mapped; - /* Insert the buffer into the regular lists */ - insert_into_queues(bh); - goto out; + /* Insert the buffer into the regular lists */ + insert_into_queues(bh); + out: + touch_buffer(bh); + return bh; + } /* * If we block while refilling the free list, somebody may * create the buffer first ... search the hashes again. */ -refill: refill_freelist(size); goto repeat; -out: - return bh; } /* -1 -> no need to flush @@ -820,11 +864,13 @@ static int balance_dirty_state(kdev_t dev) dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT; tot = nr_free_buffer_pages(); - hard_dirty_limit = tot * bdf_prm.b_un.nfract / 100; - soft_dirty_limit = hard_dirty_limit >> 1; + tot -= size_buffers_type[BUF_PROTECTED] >> PAGE_SHIFT; - if (dirty > soft_dirty_limit) - { + dirty *= 200; + soft_dirty_limit = tot * bdf_prm.b_un.nfract; + hard_dirty_limit = soft_dirty_limit * 2; + + if (dirty > soft_dirty_limit) { if (dirty > hard_dirty_limit) return 1; return 0; @@ -848,29 +894,39 @@ void balance_dirty(kdev_t dev) wakeup_bdflush(state); } -static inline void __mark_dirty(struct buffer_head *bh, int flag) +static __inline__ void __mark_dirty(struct buffer_head *bh, int flag) { bh->b_flushtime = jiffies + (flag ? bdf_prm.b_un.age_super : bdf_prm.b_un.age_buffer); - clear_bit(BH_New, &bh->b_state); refile_buffer(bh); } +/* atomic version, the user must call balance_dirty() by hand + as soon as it become possible to block */ void __mark_buffer_dirty(struct buffer_head *bh, int flag) { - __mark_dirty(bh, flag); + if (!atomic_set_buffer_dirty(bh)) + __mark_dirty(bh, flag); +} + +void mark_buffer_dirty(struct buffer_head *bh, int flag) +{ + __mark_buffer_dirty(bh, flag); + balance_dirty(bh->b_dev); } /* * A buffer may need to be moved from one buffer list to another * (e.g. in case it is not shared any more). Handle this. */ -static __inline__ void __refile_buffer(struct buffer_head *bh) +static void __refile_buffer(struct buffer_head *bh) { int dispose = BUF_CLEAN; if (buffer_locked(bh)) dispose = BUF_LOCKED; if (buffer_dirty(bh)) dispose = BUF_DIRTY; + if (buffer_protected(bh)) + dispose = BUF_PROTECTED; if (dispose != bh->b_list) { __remove_from_lru_list(bh, bh->b_list); bh->b_list = dispose; @@ -890,8 +946,6 @@ void refile_buffer(struct buffer_head *bh) */ void __brelse(struct buffer_head * buf) { - touch_buffer(buf); - if (atomic_read(&buf->b_count)) { atomic_dec(&buf->b_count); return; @@ -912,12 +966,10 @@ void __bforget(struct buffer_head * buf) write_lock(&hash_table_lock); if (!atomic_dec_and_test(&buf->b_count) || buffer_locked(buf)) goto in_use; - if (buf->b_pprev) - __hash_unlink(buf); + __hash_unlink(buf); write_unlock(&hash_table_lock); __remove_from_lru_list(buf, buf->b_list); spin_unlock(&lru_list_lock); - buf->b_state = 0; put_last_free(buf); return; @@ -1218,13 +1270,13 @@ static int create_page_buffers(int rw, struct page *page, kdev_t dev, int b[], i static void unmap_buffer(struct buffer_head * bh) { - if (buffer_mapped(bh)) - { + if (buffer_mapped(bh)) { mark_buffer_clean(bh); wait_on_buffer(bh); clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Mapped, &bh->b_state); clear_bit(BH_Req, &bh->b_state); + clear_bit(BH_New, &bh->b_state); } } @@ -1303,30 +1355,25 @@ static void create_empty_buffers(struct page *page, struct inode *inode, unsigne static void unmap_underlying_metadata(struct buffer_head * bh) { -#if 0 - if (buffer_new(bh)) { - struct buffer_head *old_bh; - - old_bh = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size); - if (old_bh) { - unmap_buffer(old_bh); - /* Here we could run brelse or bforget. We use - bforget because it will try to put the buffer - in the freelist. */ - __bforget(old_bh); - } + struct buffer_head *old_bh; + + old_bh = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size); + if (old_bh) { + unmap_buffer(old_bh); + /* Here we could run brelse or bforget. We use + bforget because it will try to put the buffer + in the freelist. */ + __bforget(old_bh); } -#endif } /* * block_write_full_page() is SMP-safe - currently it's still * being called with the kernel lock held, but the code is ready. */ -int block_write_full_page(struct dentry *dentry, struct page *page) +static int __block_write_full_page(struct inode *inode, struct page *page, get_block_t *get_block) { - struct inode *inode = dentry->d_inode; - int err, i; + int err, i, need_balance_dirty = 0; unsigned long block; struct buffer_head *bh, *head; @@ -1337,17 +1384,11 @@ int block_write_full_page(struct dentry *dentry, struct page *page) create_empty_buffers(page, inode, inode->i_sb->s_blocksize); head = page->buffers; - /* The page cache is now PAGE_CACHE_SIZE aligned, period. We handle old a.out - * and others via unaligned private mappings. - */ block = page->index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); bh = head; i = 0; do { - if (!bh) - BUG(); - /* * If the buffer isn't up-to-date, we can't be sure * that the buffer has been initialized with the proper @@ -1358,18 +1399,25 @@ int block_write_full_page(struct dentry *dentry, struct page *page) */ bh->b_end_io = end_buffer_io_sync; if (!buffer_mapped(bh)) { - err = inode->i_op->get_block(inode, block, bh, 1); + err = get_block(inode, block, bh, 1); if (err) goto out; - unmap_underlying_metadata(bh); + if (buffer_new(bh)) + unmap_underlying_metadata(bh); } set_bit(BH_Uptodate, &bh->b_state); - mark_buffer_dirty(bh,0); + if (!atomic_set_buffer_dirty(bh)) { + __mark_dirty(bh, 0); + need_balance_dirty = 1; + } bh = bh->b_this_page; block++; } while (bh != head); + if (need_balance_dirty) + balance_dirty(bh->b_dev); + SetPageUptodate(page); return 0; out: @@ -1377,13 +1425,12 @@ out: return err; } -int block_write_zero_range(struct inode *inode, struct page *page, - unsigned zerofrom, unsigned from, unsigned to, - const char * buf) +static int __block_prepare_write(struct inode *inode, struct page *page, + unsigned from, unsigned to, get_block_t *get_block) { - unsigned zeroto = 0, block_start, block_end; + unsigned block_start, block_end; unsigned long block; - int err = 0, partial = 0, need_balance_dirty = 0; + int err = 0; unsigned blocksize, bbits; struct buffer_head *bh, *head, *wait[2], **wait_bh=wait; char *kaddr = (char *)kmap(page); @@ -1396,35 +1443,31 @@ int block_write_zero_range(struct inode *inode, struct page *page, bbits = inode->i_sb->s_blocksize_bits; block = page->index << (PAGE_CACHE_SHIFT - bbits); - /* - * First pass - map what needs to be mapped, initiate reads - * on the boundaries if needed (i.e. if block is partially covered - * _and_ is not up-to-date _and_ is not new). - */ for(bh = head, block_start = 0; bh != head || !block_start; block++, block_start=block_end, bh = bh->b_this_page) { if (!bh) BUG(); block_end = block_start+blocksize; - if (block_end <= zerofrom) + if (block_end <= from) continue; if (block_start >= to) break; bh->b_end_io = end_buffer_io_sync; if (!buffer_mapped(bh)) { - err = inode->i_op->get_block(inode, block, bh, 1); + err = get_block(inode, block, bh, 1); if (err) goto out; - unmap_underlying_metadata(bh); - } - if (buffer_new(bh)) { - zeroto = block_end; - if (block_start < zerofrom) - zerofrom = block_start; - continue; + if (buffer_new(bh)) { + unmap_underlying_metadata(bh); + if (block_end > to) + memset(kaddr+to, 0, block_end-to); + if (block_start < from) + memset(kaddr+block_start, 0, from-block_start); + continue; + } } if (!buffer_uptodate(bh) && - (block_start < zerofrom || block_end > to)) { + (block_start < from || block_end > to)) { ll_rw_block(READ, 1, &bh); *wait_bh++=bh; } @@ -1438,44 +1481,31 @@ int block_write_zero_range(struct inode *inode, struct page *page, if (!buffer_uptodate(*wait_bh)) goto out; } - /* - * Now we can copy the data. - */ - if (zerofrom < from) - memset(kaddr+zerofrom, 0, from-zerofrom); - if (from < to) - err = copy_from_user(kaddr+from, buf, to-from); - if (to < zeroto) - memset(kaddr+to, 0, zeroto-to); - else - zeroto = to; - if (err < 0) - goto out; - /* - * Second pass: check if all out-of-range blocks are up-to-date - * and mark the rest up-to-date and dirty. - * - * NOTE! This also does a direct dirty balace check, - * rather than relying on bdflush just waking up every - * once in a while. This is to catch (and slow down) - * the processes that write tons of buffer.. - * - * Note how we do NOT want to do this in the full block - * case: full pages are flushed not by the people who - * dirtied them, but by people who need memory. And we - * should not penalize them for somebody else writing - * lots of dirty pages. - */ - for(bh = head, block_start = 0; + return 0; +out: + return err; +} + +static int __block_commit_write(struct inode *inode, struct page *page, + unsigned from, unsigned to) +{ + unsigned block_start, block_end; + int partial = 0, need_balance_dirty = 0; + unsigned blocksize; + struct buffer_head *bh, *head; + + blocksize = inode->i_sb->s_blocksize; + + for(bh = head = page->buffers, block_start = 0; bh != head || !block_start; block_start=block_end, bh = bh->b_this_page) { block_end = block_start + blocksize; - if (block_end <= zerofrom || block_start >= zeroto) { + if (block_end <= from || block_start >= to) { if (!buffer_uptodate(bh)) partial = 1; } else { set_bit(BH_Uptodate, &bh->b_state); - if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { + if (!atomic_set_buffer_dirty(bh)) { __mark_dirty(bh, 0); need_balance_dirty = 1; } @@ -1492,51 +1522,202 @@ int block_write_zero_range(struct inode *inode, struct page *page, */ if (!partial) SetPageUptodate(page); - kunmap(page); return 0; -out: - ClearPageUptodate(page); - kunmap(page); - return err; } -int block_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +/* + * Generic "read page" function for block devices that have the normal + * get_block functionality. This is most of the block device filesystems. + * Reads the page asynchronously --- the unlock_buffer() and + * mark_buffer_uptodate() functions propagate buffer state into the + * page struct once IO has completed. + */ +static inline int __block_read_full_page(struct inode *inode, struct page *page, + get_block_t *get_block) { - struct inode *inode = file->f_dentry->d_inode; - int err; + unsigned long iblock; + struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; + unsigned int blocksize, blocks; + unsigned long kaddr = 0; + int nr, i; if (!PageLocked(page)) - BUG(); - if (offset < 0 || offset >= PAGE_SIZE) - BUG(); - if (bytes+offset < 0 || bytes+offset > PAGE_SIZE) - BUG(); + PAGE_BUG(page); + blocksize = inode->i_sb->s_blocksize; + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + head = page->buffers; - err = block_write_range(inode, page, offset, bytes, buf); - return err ? err : bytes; + blocks = PAGE_CACHE_SIZE >> inode->i_sb->s_blocksize_bits; + iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); + bh = head; + nr = 0; + i = 0; + + do { + if (buffer_uptodate(bh)) + continue; + + if (!buffer_mapped(bh)) { + get_block(inode, iblock, bh, 0); + if (!buffer_mapped(bh)) { + if (!kaddr) + kaddr = kmap(page); + memset((char *)(kaddr + i*blocksize), 0, blocksize); + set_bit(BH_Uptodate, &bh->b_state); + continue; + } + } + + init_buffer(bh, end_buffer_io_async, NULL); + atomic_inc(&bh->b_count); + arr[nr] = bh; + nr++; + } while (i++, iblock++, (bh = bh->b_this_page) != head); + + ++current->maj_flt; + if (nr) { + if (Page_Uptodate(page)) + BUG(); + ll_rw_block(READ, nr, arr); + } else { + /* + * all buffers are uptodate - we can set the page + * uptodate as well. + */ + SetPageUptodate(page); + UnlockPage(page); + } + if (kaddr) + kunmap(page); + return 0; } /* * For moronic filesystems that do not allow holes in file. - * we allow offset==PAGE_SIZE, bytes==0 + * We may have to extend the file. */ -int block_write_cont_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +int cont_prepare_write(struct page *page, unsigned offset, unsigned to, get_block_t *get_block, unsigned long *bytes) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = (struct inode*)mapping->host; + struct page *new_page; + unsigned long pgpos; + long status; + unsigned zerofrom; + unsigned blocksize = inode->i_sb->s_blocksize; + char *kaddr; + + while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) { + status = -ENOMEM; + new_page = grab_cache_page(mapping, pgpos); + if (!new_page) + goto out; + /* we might sleep */ + if (*bytes>>PAGE_CACHE_SHIFT != pgpos) { + UnlockPage(new_page); + page_cache_release(new_page); + continue; + } + zerofrom = *bytes & ~PAGE_CACHE_MASK; + if (zerofrom & (blocksize-1)) { + *bytes |= (blocksize-1); + (*bytes)++; + } + status = __block_prepare_write(inode, new_page, zerofrom, + PAGE_CACHE_SIZE, get_block); + if (status) + goto out_unmap; + kaddr = (char*)page_address(page); + memset(kaddr+zerofrom, 0, PAGE_CACHE_SIZE-zerofrom); + __block_commit_write(inode, new_page, zerofrom, to); + kunmap(new_page); + UnlockPage(new_page); + page_cache_release(new_page); + } + + if (page->index < pgpos) { + /* completely inside the area */ + zerofrom = offset; + } else { + /* page covers the boundary, find the boundary offset */ + zerofrom = *bytes & ~PAGE_CACHE_MASK; + + /* if we will expand the thing last block will be filled */ + if (to > zerofrom && (zerofrom & (blocksize-1))) { + *bytes |= (blocksize-1); + (*bytes)++; + } + + /* starting below the boundary? Nothing to zero out */ + if (offset <= zerofrom) + zerofrom = offset; + } + status = __block_prepare_write(inode, page, zerofrom, to, get_block); + if (status) + goto out1; + kaddr = (char*)page_address(page); + if (zerofrom < offset) { + memset(kaddr+zerofrom, 0, offset-zerofrom); + __block_commit_write(inode, page, zerofrom, offset); + } + return 0; +out1: + ClearPageUptodate(page); + kunmap(page); + return status; + +out_unmap: + ClearPageUptodate(new_page); + kunmap(new_page); + UnlockPage(new_page); + page_cache_release(new_page); +out: + return status; +} + +int block_prepare_write(struct page *page, unsigned from, unsigned to, + get_block_t *get_block) { - struct inode *inode = file->f_dentry->d_inode; - int err; - unsigned zerofrom = offset; + struct inode *inode = (struct inode*)page->mapping->host; + int err = __block_prepare_write(inode, page, from, to, get_block); + if (err) { + ClearPageUptodate(page); + kunmap(page); + } + return err; +} - if (page->index > (inode->i_size >> PAGE_CACHE_SHIFT)) - zerofrom = 0; - else if (page->index == (inode->i_size >> PAGE_CACHE_SHIFT) && - offset > (inode->i_size & ~PAGE_CACHE_MASK)) - zerofrom = inode->i_size & ~PAGE_CACHE_MASK; - err = block_write_zero_range(inode, page, zerofrom,offset,offset+bytes, - buf); - return err ? err : bytes; +int generic_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + __block_commit_write((struct inode*)page->mapping->host,page,from,to); + kunmap(page); + return 0; } +int block_write_full_page(struct page *page, get_block_t *get_block) +{ + struct inode *inode = (struct inode*)page->mapping->host; + return __block_write_full_page(inode, page, get_block); +} + +int block_read_full_page(struct page *page, get_block_t *get_block) +{ + struct inode *inode = (struct inode*)page->mapping->host; + return __block_read_full_page(inode, page, get_block); +} + +int generic_block_bmap(struct address_space *mapping, long block, get_block_t *get_block) +{ + struct buffer_head tmp; + struct inode *inode = (struct inode*)mapping->host; + tmp.b_state = 0; + tmp.b_blocknr = 0; + get_block(inode, block, &tmp, 0); + return tmp.b_blocknr; +} /* * IO completion routine for a buffer_head being used for kiobuf IO: we @@ -1814,93 +1995,22 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size) return 0; } -/* - * Generic "read page" function for block devices that have the normal - * get_block functionality. This is most of the block device filesystems. - * Reads the page asynchronously --- the unlock_buffer() and - * mark_buffer_uptodate() functions propagate buffer state into the - * page struct once IO has completed. - */ -static inline int __block_read_full_page(struct inode *inode, struct page *page) -{ - unsigned long iblock; - struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; - unsigned int blocksize, blocks; - unsigned long kaddr = 0; - int nr, i; - - if (!PageLocked(page)) - PAGE_BUG(page); - blocksize = inode->i_sb->s_blocksize; - if (!page->buffers) - create_empty_buffers(page, inode, blocksize); - head = page->buffers; - - blocks = PAGE_CACHE_SIZE >> inode->i_sb->s_blocksize_bits; - iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - bh = head; - nr = 0; - i = 0; - - do { - if (buffer_uptodate(bh)) - continue; - - if (!buffer_mapped(bh)) { - inode->i_op->get_block(inode, iblock, bh, 0); - if (!buffer_mapped(bh)) { - if (!kaddr) - kaddr = kmap(page); - memset((char *)(kaddr + i*blocksize), 0, blocksize); - set_bit(BH_Uptodate, &bh->b_state); - continue; - } - } - - init_buffer(bh, end_buffer_io_async, NULL); - atomic_inc(&bh->b_count); - arr[nr] = bh; - nr++; - } while (i++, iblock++, (bh = bh->b_this_page) != head); - - ++current->maj_flt; - if (nr) { - if (Page_Uptodate(page)) - BUG(); - ll_rw_block(READ, nr, arr); - } else { - /* - * all buffers are uptodate - we can set the page - * uptodate as well. - */ - SetPageUptodate(page); - UnlockPage(page); - } - if (kaddr) - kunmap(page); - return 0; -} - -int block_read_full_page(struct dentry *dentry, struct page *page) -{ - return __block_read_full_page(dentry->d_inode, page); -} - int block_symlink(struct inode *inode, const char *symname, int len) { - struct page *page = grab_cache_page(&inode->i_data, 0); - mm_segment_t fs; + struct address_space *mapping = inode->i_mapping; + struct page *page = grab_cache_page(mapping, 0); int err = -ENOMEM; + char *kaddr; if (!page) goto fail; - fs = get_fs(); - set_fs(KERNEL_DS); - err = block_write_range(inode, page, 0, len-1, symname); - set_fs(fs); - inode->i_size = len-1; + err = mapping->a_ops->prepare_write(page, 0, len-1); if (err) - goto fail_write; + goto fail_map; + kaddr = (char*)page_address(page); + memcpy(kaddr, symname, len-1); + mapping->a_ops->commit_write(NULL, page, 0, len-1); + inode->i_size = len-1; /* * Notice that we are _not_ going to block here - end of page is * unmapped, so this will only try to map the rest of page, see @@ -1908,14 +2018,15 @@ int block_symlink(struct inode *inode, const char *symname, int len) * ->i_size will be enough for everything) and zero it out. * OTOH it's obviously correct and should make the page up-to-date. */ - err = __block_read_full_page(inode, page); + err = mapping->a_ops->readpage(NULL, page); wait_on_page(page); page_cache_release(page); if (err < 0) goto fail; mark_inode_dirty(inode); return 0; -fail_write: +fail_map: + inode->i_size = len-1; UnlockPage(page); page_cache_release(page); fail: @@ -2000,7 +2111,7 @@ out: */ int try_to_free_buffers(struct page * page) { - struct buffer_head * tmp, * bh = page->buffers; + struct buffer_head * tmp, * p, * bh = page->buffers; int index = BUFSIZE_INDEX(bh->b_size); int ret; @@ -2009,7 +2120,7 @@ int try_to_free_buffers(struct page * page) spin_lock(&free_list[index].lock); tmp = bh; do { - struct buffer_head * p = tmp; + p = tmp; tmp = tmp->b_this_page; if (buffer_busy(p)) @@ -2025,13 +2136,10 @@ int try_to_free_buffers(struct page * page) /* The buffer can be either on the regular * queues or on the free list.. */ - if (p->b_dev == B_FREE) { + if (p->b_dev != B_FREE) + __remove_from_queues(p); + else __remove_from_free_list(p, index); - } else { - if (p->b_pprev) - __hash_unlink(p); - __remove_from_lru_list(p, p->b_list); - } __put_unused_buffer_head(p); } while (tmp != bh); spin_unlock(&unused_list_lock); @@ -2051,7 +2159,8 @@ out: busy_buffer_page: /* Uhhuh, start writeback so that we don't end up with all dirty pages */ - wakeup_bdflush(0); + if (buffer_dirty(p)) + wakeup_bdflush(0); ret = 0; goto out; } @@ -2065,7 +2174,7 @@ void show_buffers(void) int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0; int protected = 0; int nlist; - static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY" }; + static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY", "PROTECTED", }; #endif printk("Buffer memory: %6dkB\n", @@ -2091,10 +2200,16 @@ void show_buffers(void) used++, lastused = found; bh = bh->b_next_free; } while (bh != lru_list[nlist]); - printk("%8s: %d buffers, %d used (last=%d), " + { + int tmp = nr_buffers_type[nlist]; + if (found != tmp) + printk("%9s: BUG -> found %d, reported %d\n", + buf_types[nlist], found, tmp); + } + printk("%9s: %d buffers, %lu kbyte, %d used (last=%d), " "%d locked, %d protected, %d dirty\n", - buf_types[nlist], found, used, lastused, - locked, protected, dirty); + buf_types[nlist], found, size_buffers_type[nlist]>>10, + used, lastused, locked, protected, dirty); } spin_unlock(&lru_list_lock); #endif @@ -2184,8 +2299,7 @@ void wakeup_bdflush(int block) if (current == bdflush_tsk) return; - if (!block) - { + if (!block) { wake_up_process(bdflush_tsk); return; } @@ -2210,7 +2324,7 @@ void wakeup_bdflush(int block) as all dirty buffers lives _only_ in the DIRTY lru list. As we never browse the LOCKED and CLEAN lru lists they are infact completly useless. */ -static void flush_dirty_buffers(int check_flushtime) +static int flush_dirty_buffers(int check_flushtime) { struct buffer_head * bh, *next; int flushed = 0, i; @@ -2220,29 +2334,24 @@ static void flush_dirty_buffers(int check_flushtime) bh = lru_list[BUF_DIRTY]; if (!bh) goto out_unlock; - for (i = nr_buffers_type[BUF_DIRTY]; i-- > 0; bh = next) - { + for (i = nr_buffers_type[BUF_DIRTY]; i-- > 0; bh = next) { next = bh->b_next_free; - if (!buffer_dirty(bh)) - { + if (!buffer_dirty(bh)) { __refile_buffer(bh); continue; } if (buffer_locked(bh)) continue; - if (check_flushtime) - { - /* The dirty lru list is chronogical ordered so + if (check_flushtime) { + /* The dirty lru list is chronologically ordered so if the current bh is not yet timed out, then also all the following bhs will be too young. */ if (time_before(jiffies, bh->b_flushtime)) goto out_unlock; - } - else - { + } else { if (++flushed > bdf_prm.b_un.ndirty) goto out_unlock; } @@ -2259,6 +2368,8 @@ static void flush_dirty_buffers(int check_flushtime) } out_unlock: spin_unlock(&lru_list_lock); + + return flushed; } /* @@ -2342,6 +2453,7 @@ asmlinkage long sys_bdflush(int func, long data) */ int bdflush(void * unused) { + int flushed; /* * We have a bare-bones task_struct, and really should fill * in a few more things so "top" and /proc/2/{exe,root,cwd} @@ -2363,7 +2475,7 @@ int bdflush(void * unused) for (;;) { CHECK_EMERGENCY_SYNC - flush_dirty_buffers(0); + flushed = flush_dirty_buffers(0); /* If wakeup_bdflush will wakeup us after our bdflush_done wakeup, then @@ -2378,10 +2490,10 @@ int bdflush(void * unused) /* * If there are still a lot of dirty buffers around, * skip the sleep and flush some more. Otherwise, we - * sleep for a while. + * go to sleep waiting a wakeup. */ - if (balance_dirty_state(NODEV) < 0) - schedule_timeout(5*HZ); + if (!flushed || balance_dirty_state(NODEV) < 0) + schedule(); /* Remember to mark us as running otherwise the next schedule will block. */ __set_current_state(TASK_RUNNING); @@ -2413,24 +2525,19 @@ int kupdate(void * unused) for (;;) { /* update interval */ interval = bdf_prm.b_un.interval; - if (interval) - { + if (interval) { tsk->state = TASK_INTERRUPTIBLE; schedule_timeout(interval); - } - else - { + } else { stop_kupdate: tsk->state = TASK_STOPPED; schedule(); /* wait for SIGCONT */ } /* check for sigstop */ - if (signal_pending(tsk)) - { + if (signal_pending(tsk)) { int stopped = 0; spin_lock_irq(&tsk->sigmask_lock); - if (sigismember(&tsk->signal, SIGSTOP)) - { + if (sigismember(&tsk->signal, SIGSTOP)) { sigdelset(&tsk->signal, SIGSTOP); stopped = 1; } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index faa983e0f..88e954eeb 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -39,12 +39,62 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) inode->i_op = &coda_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &coda_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &coda_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &coda_symlink_aops; + } else init_special_inode(inode, inode->i_mode, attr->va_rdev); } +struct inode * coda_iget(struct super_block * sb, ViceFid * fid, + struct coda_vattr * attr) +{ + struct inode *inode; + struct coda_sb_info *sbi= coda_sbp(sb); + struct coda_inode_info *cii; + ino_t ino = attr->va_fileid; + + inode = iget(sb, ino); + if ( !inode ) { + CDEBUG(D_CNODE, "coda_iget: no inode\n"); + return NULL; + } + + /* check if the inode is already initialized */ + cii = ITOC(inode); + if (cii->c_magic == CODA_CNODE_MAGIC) + goto out; + + /* Initialize the Coda inode info structure */ + memset(cii, 0, (int) sizeof(struct coda_inode_info)); + cii->c_magic = CODA_CNODE_MAGIC; + cii->c_fid = *fid; + cii->c_flags = 0; + cii->c_vnode = inode; + INIT_LIST_HEAD(&(cii->c_cnhead)); + INIT_LIST_HEAD(&(cii->c_volrootlist)); + + coda_fill_inode(inode, attr); + + /* check if it is a weird fid (hashed fid != ino), f.i mountpoints + repair object, expanded local-global conflict trees, etc. + */ + if ( coda_f2i(fid) == ino ) + goto out; + + /* check if we expect this weird fid */ + if ( !coda_fid_is_weird(fid) ) + printk("Coda: unknown weird fid: ino %ld, fid %s." + "Tell Peter.\n", (long)ino, coda_f2s(&cii->c_fid)); + + /* add the inode to a global list so we can find it back later */ + list_add(&cii->c_volrootlist, &sbi->sbi_volroothead); + CDEBUG(D_CNODE, "Added %ld, %s to volroothead\n", + (long)ino, coda_f2s(&cii->c_fid)); +out: + return inode; +} + /* this is effectively coda_iget: - get attributes (might be cached) - get the inode for the fid using vfs iget @@ -54,16 +104,12 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) { struct coda_inode_info *cnp; - struct coda_sb_info *sbi= coda_sbp(sb); struct coda_vattr attr; int error; - ino_t ino; ENTRY; - /* - * We get inode numbers from Venus -- see venus source - */ + /* We get inode numbers from Venus -- see venus source */ error = venus_getattr(sb, fid, &attr); if ( error ) { @@ -74,53 +120,29 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) return error; } - ino = attr.va_fileid; - *inode = iget(sb, ino); - if ( !*inode ) { - printk("coda_cnode_make: iget failed\n"); + *inode = coda_iget(sb, fid, &attr); + if ( !(*inode) ) { + printk("coda_cnode_make: coda_iget failed\n"); return -ENOMEM; } cnp = ITOC(*inode); - /* see if we've got it already */ - if ( cnp->c_magic != 0 && coda_fideq(fid, &cnp->c_fid)) { + /* see if it is the right one (we might have an inode collision) */ + if ( coda_fideq(fid, &cnp->c_fid) ) { + CDEBUG(D_DOWNCALL, + "Done making inode: ino %ld, count %d with %s\n", + (*inode)->i_ino, (*inode)->i_count, + coda_f2s(&cnp->c_fid)); + EXIT; return 0; } - /* not fresh: collision */ - if ( cnp->c_magic != 0 ) { - printk("coda_cnode_make on initialized inode %ld, old %s new -%s!\n", + /* collision */ + printk("coda_cnode_make on initialized inode %ld, old %s new %s!\n", (*inode)->i_ino, coda_f2s(&cnp->c_fid), coda_f2s2(fid)); iput(*inode); - return -ENOENT; - } - - memset(cnp, 0, (int) sizeof(struct coda_inode_info)); - cnp->c_fid = *fid; - cnp->c_magic = CODA_CNODE_MAGIC; - cnp->c_flags = 0; - cnp->c_vnode = *inode; - INIT_LIST_HEAD(&(cnp->c_cnhead)); - INIT_LIST_HEAD(&(cnp->c_volrootlist)); - - /* fill in the inode attributes */ - if ( coda_f2i(fid) != ino ) { - if ( !coda_fid_is_weird(fid) ) - printk("Coda: unknown weird fid: ino %ld, fid %s." - "Tell Peter.\n", (long)ino, coda_f2s(&cnp->c_fid)); - list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); - CDEBUG(D_UPCALL, "Added %ld ,%s to volroothead\n", - (long)ino, coda_f2s(&cnp->c_fid)); - } - - coda_fill_inode(*inode, &attr); - CDEBUG(D_DOWNCALL, "Done making inode: ino %ld, count %d with %s\n", - (*inode)->i_ino, (*inode)->i_count, - coda_f2s(&cnp->c_fid)); - EXIT; - return 0; + return -ENOENT; } @@ -183,11 +205,13 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) coda_f2s(&cii->c_fid), cii->c_vnode->i_ino); if ( coda_fideq(&cii->c_fid, fid) ) { inode = cii->c_vnode; - CDEBUG(D_INODE, "volume root, found %ld\n", cii->c_vnode->i_ino); + CDEBUG(D_INODE, "volume root, found %ld\n", inode->i_ino); if ( cii->c_magic != CODA_CNODE_MAGIC ) printk("%s: Bad magic in inode, tell Peter.\n", __FUNCTION__); - return cii->c_vnode; + + iget(sb, inode->i_ino); + return inode; } } @@ -224,7 +248,6 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) } CDEBUG(D_INODE, "found %ld\n", inode->i_ino); - iput(inode); return inode; } diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 5f2f3c467..4a7bebb62 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -102,7 +102,6 @@ int coda_fid_is_weird(struct ViceFid *fid) return 1; return 0; - } @@ -290,7 +289,6 @@ void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) vattr->va_ctime.tv_sec = iattr->ia_ctime; vattr->va_ctime.tv_nsec = 0; } - } void print_vattr(struct coda_vattr *attr) diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 2a1a122c8..3eecd4a5a 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -77,26 +77,16 @@ struct inode_operations coda_dir_inode_operations = coda_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ coda_permission, /* permission */ coda_revalidate_inode /* revalidate */ }; struct file_operations coda_dir_operations = { - NULL, /* lseek */ - NULL, /* read -- bad */ - NULL, /* write */ - coda_readdir, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - NULL, /* mmap */ - coda_open, /* open */ - NULL, - coda_release, /* release */ - coda_fsync, /* fsync */ + readdir: coda_readdir, + open: coda_open, + release: coda_release, + fsync: coda_fsync, }; @@ -206,7 +196,6 @@ int coda_permission(struct inode *inode, int mask) } - /* creation routines: create, mknod, mkdir, link, symlink */ static int coda_create(struct inode *dir, struct dentry *de, int mode) @@ -267,7 +256,8 @@ static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev) coda_vfs_stat.create++; - CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n",name, length, mode, rdev); + CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n", + name, length, mode, rdev); if (coda_isroot(dir) && coda_iscontrol(name, length)) return -EPERM; @@ -528,7 +518,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, } - /* file operations for directories */ int coda_readdir(struct file *file, void *dirent, filldir_t filldir) { @@ -607,11 +596,11 @@ int coda_open(struct inode *i, struct file *f) coda_load_creds(cred); f->private_data = cred; - if ( cnp->c_ovp ) { + if ( cnp->c_ovp ) iput(cnp->c_ovp); - cnp->c_ovp = NULL; - } + cnp->c_ovp = cont_inode; + i->i_mapping = cont_inode->i_mapping; cnp->c_ocount++; CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", @@ -626,7 +615,7 @@ int coda_open(struct inode *i, struct file *f) int coda_release(struct inode *i, struct file *f) { struct coda_inode_info *cnp; - int error; + int error = 0; unsigned short flags = (f->f_flags) & (~O_EXCL); unsigned short cflags = coda_flags_to_cflags(flags); struct coda_cred *cred; @@ -652,14 +641,17 @@ int coda_release(struct inode *i, struct file *f) --cnp->c_ocount; - if (flags & (O_WRONLY | O_RDWR)) { + if ( flags & (O_WRONLY | O_RDWR) ) --cnp->c_owrite; - } + /* Venus closing a container file? don't bother making the upcall. */ + if ( current->pid != coda_upc_comm.vc_pid ) { error = venus_release(i->i_sb, &(cnp->c_fid), cflags, cred); + } - CODA_FREE(cred, sizeof(*cred)); f->private_data = NULL; + if (cred) + CODA_FREE(cred, sizeof(*cred)); CDEBUG(D_FILE, "coda_release: result: %d\n", error); return error; @@ -857,19 +849,18 @@ int coda_revalidate_inode(struct dentry *dentry) if ( cii->c_flags == 0 ) return 0; - /* Venus closed the device .... */ - if ( cii->c_flags & C_DYING ) { - make_bad_inode(inode); - return -EIO; - } + /* Venus accessing a container file, don't try to revalidate */ + if ( current->pid == coda_upc_comm.vc_pid ) + return 0; + /* Venus closed the device .... */ + if ( cii->c_flags & C_DYING ) + goto return_bad_inode; if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) { error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr); - if ( error ) { - make_bad_inode(inode); - return -EIO; - } + if ( error ) + goto return_bad_inode; /* this inode may be lost if: - it's ino changed @@ -887,12 +878,9 @@ int coda_revalidate_inode(struct dentry *dentry) } /* the following can happen when a local fid is replaced - with a global one, here we lose and declar the inode bad */ - if (inode->i_ino != old_ino) { - make_bad_inode(inode); - inode->i_mode = old_mode; - return -EIO; - } + with a global one, here we lose and declare the inode bad */ + if (inode->i_ino != old_ino) + goto return_bad_inode; if ( cii->c_flags ) coda_flag_inode_children(inode, C_FLUSH); @@ -901,5 +889,14 @@ int coda_revalidate_inode(struct dentry *dentry) } return 0; + +return_bad_inode: + if ( cii->c_ovp ) { + iput(cii->c_ovp); + inode->i_mapping = &inode->i_data; + cii->c_ovp = NULL; + } + make_bad_inode(inode); + return -EIO; } diff --git a/fs/coda/file.c b/fs/coda/file.c index 726bd18fd..82f661111 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -14,6 +14,7 @@ #include <linux/stat.h> #include <linux/errno.h> #include <linux/locks.h> +#include <linux/smp_lock.h> #include <asm/segment.h> #include <linux/string.h> #include <asm/uaccess.h> @@ -26,9 +27,6 @@ #include <linux/coda_proc.h> /* file operations */ -static int coda_readpage(struct dentry *dentry, struct page * page); -static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off); -static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off); static int coda_file_mmap(struct file * file, struct vm_area_struct * vma); /* also exported from this file (used for dirs) */ @@ -47,57 +45,21 @@ struct inode_operations coda_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - coda_readpage, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ coda_permission, /* permission */ coda_revalidate_inode /* revalidate */ }; struct file_operations coda_file_operations = { - NULL, /* lseek - default should work for coda */ - coda_file_read, /* read */ - coda_file_write, /* write */ - NULL, /* readdir */ - NULL, /* select - default */ - NULL, /* ioctl */ - coda_file_mmap, /* mmap */ - coda_open, /* open */ - NULL, - coda_release, /* release */ - coda_fsync, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ + read: generic_file_read, + write: generic_file_write, + mmap: coda_file_mmap, + open: coda_open, + release: coda_release, + fsync: coda_fsync, }; - -/* File file operations */ -static int coda_readpage(struct dentry * dentry, struct page * page) -{ - struct inode *coda_inode = dentry->d_inode; - struct dentry cont_dentry; - struct coda_inode_info *cii; - - ENTRY; - coda_vfs_stat.readpage++; - - cii = ITOC(coda_inode); - - if ( ! cii->c_ovp ) { - printk("coda_readpage: no open inode for ino %ld, %s\n", - coda_inode->i_ino, dentry->d_name.name); - return -ENXIO; - } - - cont_dentry.d_inode = cii->c_ovp; - - CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", - coda_inode->i_ino, cii->c_ovp->i_ino, page->index); - - block_read_full_page(&cont_dentry, page); - EXIT; - return 0; -} + +/* File operations */ static int coda_file_mmap(struct file * file, struct vm_area_struct * vma) { @@ -115,89 +77,6 @@ static int coda_file_mmap(struct file * file, struct vm_area_struct * vma) return res; } -static ssize_t coda_file_read(struct file *coda_file, char *buff, - size_t count, loff_t *ppos) -{ - struct coda_inode_info *cnp; - struct inode *coda_inode = coda_file->f_dentry->d_inode; - struct inode *cont_inode = NULL; - struct file cont_file; - struct dentry cont_dentry; - int result = 0; - - ENTRY; - coda_vfs_stat.file_read++; - - cnp = ITOC(coda_inode); - CHECK_CNODE(cnp); - - cont_inode = cnp->c_ovp; - if ( cont_inode == NULL ) { - printk("coda_file_read: cached inode is 0!\n"); - return -1; - } - - coda_prepare_openfile(coda_inode, coda_file, cont_inode, - &cont_file, &cont_dentry); - - if (!cont_file.f_op || ! cont_file.f_op->read) { - printk( "container file has no read in file operations.\n"); - return -1; - } - - result = cont_file.f_op->read(&cont_file , buff, count, - &(cont_file.f_pos)); - - CDEBUG(D_FILE, "ops at %p result %d, count %ld, position: %d\n", - cont_file.f_op, result, (long)count, (int)cont_file.f_pos); - - coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); - return result; -} - - -static ssize_t coda_file_write(struct file *coda_file, const char *buff, - size_t count, loff_t *ppos) -{ - struct coda_inode_info *cnp; - struct inode *coda_inode = coda_file->f_dentry->d_inode; - struct inode *cont_inode = NULL; - struct file cont_file; - struct dentry cont_dentry; - int result = 0; - - ENTRY; - coda_vfs_stat.file_write++; - - cnp = ITOC(coda_inode); - CHECK_CNODE(cnp); - - cont_inode = cnp->c_ovp; - if ( cont_inode == NULL ) { - printk("coda_file_write: cached inode is 0!\n"); - return -1; - } - - coda_prepare_openfile(coda_inode, coda_file, cont_inode, - &cont_file, &cont_dentry); - - if (!cont_file.f_op || !cont_file.f_op->write) { - printk("coda_file_write: container file has no file ops.\n"); - return -1; - } - - down(&cont_inode->i_sem); - result = cont_file.f_op->write(&cont_file , buff, count, - &(cont_file.f_pos)); - up(&cont_inode->i_sem); - coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); - - if (result) - cnp->c_flags |= C_VATTR; - - return result; -} - int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) { struct coda_inode_info *cnp; @@ -213,12 +92,14 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) S_ISLNK(coda_inode->i_mode))) return -EINVAL; + lock_kernel(); cnp = ITOC(coda_inode); CHECK_CNODE(cnp); cont_inode = cnp->c_ovp; if ( cont_inode == NULL ) { printk("coda_file_write: cached inode is 0!\n"); + unlock_kernel(); return -1; } @@ -235,6 +116,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) up(&cont_inode->i_sem); coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); + unlock_kernel(); return result; } /* @@ -284,8 +166,8 @@ int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind) *ind = iget(sbptr, ino); if ( *ind == NULL ) { - printk("coda_inode_grab: iget(dev: %d, ino: %ld) - returns NULL.\n", dev, (long)ino); + printk("coda_inode_grab: iget(dev: %d, ino: %ld) " + "returns NULL.\n", dev, (long)ino); return -ENOENT; } CDEBUG(D_FILE, "ino: %ld, ops at %p\n", (long)ino, (*ind)->i_op); diff --git a/fs/coda/inode.c b/fs/coda/inode.c index e2f2931b1..b389b623e 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -52,7 +52,9 @@ struct super_operations coda_super_operations = coda_put_super, /* put_super */ NULL, /* write_super */ coda_statfs, /* statfs */ - NULL /* remount_fs */ + NULL, /* remount_fs */ + NULL, /* no clear inode */ + NULL /* umount attempt begin */ }; static struct super_block * coda_read_super(struct super_block *sb, @@ -163,15 +165,16 @@ static void coda_read_inode(struct inode *inode) return; } -static void coda_put_inode(struct inode *in) +static void coda_put_inode(struct inode *inode) { ENTRY; - CDEBUG(D_INODE,"ino: %ld, count %d\n", in->i_ino, in->i_count); - - if ( in->i_count == 1 ) - in->i_nlink = 0; + CDEBUG(D_INODE,"ino: %ld, count %d\n", inode->i_ino, inode->i_count); + if ( inode->i_count == 1 ) { + write_inode_now(inode); + inode->i_nlink = 0; + } } static void coda_delete_inode(struct inode *inode) @@ -189,15 +192,17 @@ static void coda_delete_inode(struct inode *inode) return; } - - if ( ! list_empty(&cii->c_volrootlist) ) + if ( ! list_empty(&cii->c_volrootlist) ) { list_del(&cii->c_volrootlist); + INIT_LIST_HEAD(&cii->c_volrootlist); + } open_inode = cii->c_ovp; if ( open_inode ) { CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n", open_inode->i_ino, open_inode->i_count); cii->c_ovp = NULL; + inode->i_mapping = &inode->i_data; iput(open_inode); } diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index cec92b7f4..9a900a91a 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -46,26 +46,15 @@ struct inode_operations coda_ioctl_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ coda_ioctl_permission, /* permission */ NULL /* revalidate */ }; struct file_operations coda_ioctl_operations = { - NULL, /* lseek - default should work for coda */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select - default */ - coda_pioctl, /* ioctl */ - NULL, /* mmap */ - coda_ioctl_open, /* open */ - NULL, - coda_ioctl_release, /* release */ - NULL, /* fsync */ + ioctl: coda_pioctl, + open: coda_ioctl_open, + release: coda_ioctl_release, }; /* the coda pioctl inode ops */ @@ -79,7 +68,6 @@ static int coda_ioctl_permission(struct inode *inode, int mask) /* The pioctl file ops*/ int coda_ioctl_open(struct inode *i, struct file *f) { - ENTRY; CDEBUG(D_PIOCTL, "File inode number: %ld\n", diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 65aeee08e..07ba22335 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -76,6 +76,21 @@ static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) return mask; } +static int coda_psdev_ioctl(struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + unsigned int data; + + switch(cmd) { + case CIOC_KERNEL_VERSION: + data = CODA_KERNEL_VERSION; + return put_user(data, (int *) arg); + default: + return -ENOTTY; + } + + return 0; +} /* * Receive a message written by Venus to the psdev @@ -246,7 +261,6 @@ static int coda_psdev_open(struct inode * inode, struct file * file) vcp->vc_inuse++; MOD_INC_USE_COUNT; - if ( file->f_flags == O_RDWR ) { vcp->vc_pid = current->pid; vcp->vc_seq = 0; @@ -262,7 +276,6 @@ static int coda_psdev_open(struct inode * inode, struct file * file) } - static int coda_psdev_release(struct inode * inode, struct file * file) { struct venus_comm *vcp = &coda_upc_comm; @@ -298,6 +311,7 @@ static int coda_psdev_release(struct inode * inode, struct file * file) CODA_FREE(req, (u_int)sizeof(struct upc_req)); continue; } + req->uc_flags |= REQ_ABORT; wake_up(&req->uc_sleep); } @@ -305,6 +319,7 @@ static int coda_psdev_release(struct inode * inode, struct file * file) CDEBUG(D_PSDEV, "wake up processing clients\n"); while ( (lh = lh->next) != &vcp->vc_processing) { req = list_entry(lh, struct upc_req, uc_chain); + req->uc_flags |= REQ_ABORT; wake_up(&req->uc_sleep); } CDEBUG(D_PSDEV, "Done.\n"); @@ -315,19 +330,12 @@ static int coda_psdev_release(struct inode * inode, struct file * file) static struct file_operations coda_psdev_fops = { - NULL, /* llseek */ - coda_psdev_read, /* read */ - coda_psdev_write, /* write */ - NULL, /* coda_psdev_readdir */ - coda_psdev_poll, /* poll */ - NULL, /* ioctl */ - NULL, /* coda_psdev_mmap */ - coda_psdev_open, /* open */ - NULL, - coda_psdev_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ + read: coda_psdev_read, + write: coda_psdev_write, + poll: coda_psdev_poll, + ioctl: coda_psdev_ioctl, + open: coda_psdev_open, + release: coda_psdev_release, }; diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index 872370cf9..d44d0f7c3 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -48,8 +48,6 @@ fail: return error; } -struct inode_operations coda_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, +struct address_space_operations coda_symlink_aops = { readpage: coda_symlink_filler }; diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index 046153362..289f9417c 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -109,7 +109,8 @@ char *coda_upcall_names[] = { "open_by_path", /* 31 */ "resolve ", /* 32 */ "reintegrate ", /* 33 */ - "statfs " /* 34 */ + "statfs ", /* 34 */ + "make_cinode " /* 35 */ }; @@ -214,8 +215,8 @@ unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pen return 0; time_avg = get_time_average( pentry ); - return - sqr_root( (pentry->time_squared_sum / pentry->count) - + + return sqr_root( (pentry->time_squared_sum / pentry->count) - time_avg * time_avg ); } diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 9aecd8a6e..63586d05c 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -24,6 +24,7 @@ #include <linux/mm.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/stat.h> #include <linux/errno.h> #include <linux/locks.h> @@ -392,7 +393,7 @@ int venus_readlink(struct super_block *sb, struct ViceFid *fid, if ( retlen > *length ) retlen = *length; *length = retlen; - result = (char *)outp + (int)outp->coda_readlink.data; + result = (char *)outp + (long)outp->coda_readlink.data; memcpy(buffer, result, retlen); *(buffer + retlen) = '\0'; } @@ -541,7 +542,7 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, inp->coda_ioctl.data = (char *)(INSIZE(ioctl)); /* get the data out of user space */ - if ( copy_from_user((char*)inp + (int)inp->coda_ioctl.data, + if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data, data->vi.in, data->vi.in_size) ) { error = EINVAL; goto exit; @@ -567,7 +568,7 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, if ( error ) goto exit; if (copy_to_user(data->vi.out, - (char *)outp + (int)outp->coda_ioctl.data, + (char *)outp + (long)outp->coda_ioctl.data, data->vi.out_size)) { error = EINVAL; goto exit; @@ -630,7 +631,7 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) set_current_state(TASK_UNINTERRUPTIBLE); /* got a reply */ - if ( vmp->uc_flags & REQ_WRITE ) + if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) break; if ( !coda_hard && signal_pending(current) ) { @@ -660,7 +661,8 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) } CDEBUG(D_SPECIAL, "begin: %ld.%06ld, elapsed: %ld.%06ld\n", - begin.tv_sec, begin.tv_usec, end.tv_sec, end.tv_usec); + begin.tv_sec, (unsigned long)begin.tv_usec, + end.tv_sec, (unsigned long)end.tv_usec); return ((end.tv_sec * 1000000) + end.tv_usec); } @@ -844,7 +846,6 @@ ENTRY; int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { - /* Handle invalidation requests. */ if ( !sb || !sb->s_root || !sb->s_root->d_inode) { printk("coda_downcall: opcode %d, no sb!\n", opcode); @@ -877,7 +878,8 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_ZAPDIR : { struct inode *inode; ViceFid *fid = &out->coda_zapdir.CodaFid; - CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", coda_f2s(fid)); + CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", + coda_f2s(fid)); clstats(CODA_ZAPDIR); inode = coda_fid_to_inode(fid, sb); @@ -887,6 +889,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) coda_flag_inode_children(inode, C_PURGE); CDEBUG(D_DOWNCALL, "zapdir: inode = %ld cache cleared\n", inode->i_ino); coda_flag_inode(inode, C_VATTR); + iput(inode); } else CDEBUG(D_DOWNCALL, "zapdir: no inode\n"); @@ -897,11 +900,14 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) struct inode *inode; struct ViceFid *fid = &out->coda_zapfile.CodaFid; clstats(CODA_ZAPFILE); - CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid)); + CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", + coda_f2s(fid)); inode = coda_fid_to_inode(fid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n", inode->i_ino); + CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n", + inode->i_ino); coda_flag_inode(inode, C_VATTR); + iput(inode); } else CDEBUG(D_DOWNCALL, "zapfile: no inode\n"); return 0; @@ -910,18 +916,61 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_PURGEFID : { struct inode *inode; ViceFid *fid = &out->coda_purgefid.CodaFid; - CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid)); + CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", + coda_f2s(fid)); clstats(CODA_PURGEFID); inode = coda_fid_to_inode(fid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", inode->i_ino); + CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", + inode->i_ino); coda_flag_inode_children(inode, C_PURGE); coda_purge_dentries(inode); + iput(inode); }else CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); return 0; } + case CODA_MAKE_CINODE : { + struct inode *inode; + ViceFid *fid = &out->coda_make_cinode.CodaFid; + struct coda_vattr *attr = &out->coda_make_cinode.attr; + int fd = out->coda_make_cinode.fd; + struct file *file; + CDEBUG(D_DOWNCALL, "make_cinode: fid = %s, ino = %ld\n", + coda_f2s(fid), attr->va_fileid); + + inode = coda_iget(sb, fid, attr); + if ( !inode ) { + CDEBUG(D_DOWNCALL, "make_cinode: no inode\n"); + return -EINVAL; + } + + file = fget(fd); + if ( !file ) { + CDEBUG(D_DOWNCALL, "make_cinode: no file\n"); + iput(inode); + return -EINVAL; + } + + inode->u.coda_i.c_ovp = file->f_dentry->d_inode; + inode->i_mapping = file->f_dentry->d_inode->i_mapping; + file->f_dentry->d_inode = inode; + file->f_op = &coda_file_operations; + + /* + Unhash the dentry of the container file, as it is + still owned by the fs that stores the container + file. A more reliable solution would be to create + an new dentry owned by Coda, but that would require + knowledge of the internals of the dcache. + */ + d_drop(file->f_dentry); + + fput(file); + return 0; + } + case CODA_REPLACE : { struct inode *inode; ViceFid *oldfid = &out->coda_replace.OldFid; @@ -932,6 +981,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) if ( inode ) { CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", inode->i_ino); coda_replace_fid(inode, oldfid, newfid); + iput(inode); }else CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); @@ -941,5 +991,3 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) return 0; } - - diff --git a/fs/cramfs/inflate/zconf.h b/fs/cramfs/inflate/zconf.h index adc70c276..0b5ec8838 100644 --- a/fs/cramfs/inflate/zconf.h +++ b/fs/cramfs/inflate/zconf.h @@ -83,8 +83,8 @@ typedef uLong FAR uLongf; typedef void FAR *voidpf; typedef void *voidp; -#include <sys/types.h> /* for off_t */ -#include <unistd.h> /* for SEEK_* and off_t */ +#include <linux/types.h> /* for off_t */ +#include <linux/unistd.h> /* for SEEK_* and off_t */ #define z_off_t off_t #endif /* _ZCONF_H */ diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 07c8f15a1..b9ab3c4c4 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -25,7 +25,7 @@ static struct super_operations cramfs_ops; static struct inode_operations cramfs_file_inode_operations; static struct inode_operations cramfs_dir_inode_operations; -static struct inode_operations cramfs_symlink_inode_operations; +static struct address_space_operations cramfs_aops; /* These two macros may change in future, to provide better st_ino semantics. */ @@ -51,13 +51,15 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod result in GNU find, even without -noleaf option. */ insert_inode_hash(inode); - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &cramfs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_data.a_ops = &cramfs_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &cramfs_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &cramfs_symlink_inode_operations; - else { + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &cramfs_aops; + } else { inode->i_size = 0; init_special_inode(inode, inode->i_mode, cramfs_inode->size); } @@ -344,53 +346,9 @@ static int cramfs_readpage(struct dentry *dentry, struct page * page) return 0; } -static struct page *get_symlink_page(struct dentry *dentry) -{ - return read_cache_page(&dentry->d_inode->i_data, 0, (filler_t *)cramfs_readpage, dentry); -} - -static int cramfs_readlink(struct dentry *dentry, char *buffer, int len) -{ - struct inode *inode = dentry->d_inode; - int retval; - - if (!inode || !S_ISLNK(inode->i_mode)) - return -EBADF; - - retval = inode->i_size; - if (retval) { - int len; - struct page *page = get_symlink_page(dentry); - - if (IS_ERR(page)) - return PTR_ERR(page); - wait_on_page(page); - len = retval; - retval = -EIO; - if (Page_Uptodate(page)) { - retval = -EFAULT; - if (!copy_to_user(buffer, (void *) page_address(page), len)) - retval = len; - } - page_cache_release(page); - } - return retval; -} - -static struct dentry *cramfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) -{ - struct page *page = get_symlink_page(dentry); - struct dentry *result; - - if (IS_ERR(page)) { - dput(base); - return ERR_PTR(PTR_ERR(page)); - } - - result = lookup_dentry((void *) page_address(page), base, follow); - page_cache_release(page); - return result; -} +static struct address_space_operations cramfs_aops = { + readpage: cramfs_readpage +}; /* * Our operations: @@ -398,99 +356,25 @@ static struct dentry *cramfs_follow_link(struct dentry *dentry, struct dentry *b * A regular file can be read and mmap'ed. */ static struct file_operations cramfs_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - NULL, /* write - bad */ - NULL, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ + read: generic_file_read, + mmap: generic_file_mmap, }; /* * A directory can only readdir */ static struct file_operations cramfs_directory_operations = { - NULL, /* lseek - default */ - NULL, /* read */ - NULL, /* write - bad */ - cramfs_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ + readdir: cramfs_readdir, }; static struct inode_operations cramfs_file_inode_operations = { &cramfs_file_operations, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - cramfs_readpage, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static struct inode_operations cramfs_dir_inode_operations = { &cramfs_directory_operations, NULL, /* create */ cramfs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ -}; - -static struct inode_operations cramfs_symlink_inode_operations = { - NULL, /* symlinks do not have files */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - cramfs_readlink, /* readlink */ - cramfs_follow_link, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static struct super_operations cramfs_ops = { diff --git a/fs/devices.c b/fs/devices.c index 36e15475a..607a96edd 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -157,7 +157,7 @@ int chrdev_open(struct inode * inode, struct file * filp) * depending on the special file... */ static struct file_operations def_chr_fops = { - open: chrdev_open + open: chrdev_open, }; static struct inode_operations chrdev_inode_operations = { diff --git a/fs/devpts/root.c b/fs/devpts/root.c index 92978217f..5af3967e2 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -21,31 +21,13 @@ static struct dentry *devpts_root_lookup(struct inode *,struct dentry *); static int devpts_revalidate(struct dentry *, int); static struct file_operations devpts_root_operations = { - NULL, /* llseek */ - NULL, /* read */ - NULL, /* write */ - devpts_root_readdir, /* readdir */ + readdir: devpts_root_readdir, }; struct inode_operations devpts_root_inode_operations = { &devpts_root_operations, /* file operations */ NULL, /* create */ devpts_root_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static struct dentry_operations devpts_dentry_operations = { diff --git a/fs/efs/dir.c b/fs/efs/dir.c index 4e84b1abb..1283f9fdc 100644 --- a/fs/efs/dir.c +++ b/fs/efs/dir.c @@ -9,41 +9,13 @@ static int efs_readdir(struct file *, void *, filldir_t); static struct file_operations efs_dir_operations = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - efs_readdir, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ + readdir: efs_readdir, }; -extern int efs_get_block(struct inode *, long, struct buffer_head *, int); - struct inode_operations efs_dir_inode_operations = { &efs_dir_operations, /* default directory file-ops */ NULL, /* create */ efs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - efs_get_block, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir) { diff --git a/fs/efs/file.c b/fs/efs/file.c index 8a2a8163d..3c6de9b28 100644 --- a/fs/efs/file.c +++ b/fs/efs/file.c @@ -62,37 +62,10 @@ int efs_bmap(struct inode *inode, efs_block_t block) { } static struct file_operations efs_file_operations = { - NULL, /* lseek */ - generic_file_read, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ + read: generic_file_read, + mmap: generic_file_mmap, }; struct inode_operations efs_file_inode_operations = { &efs_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - efs_get_block, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/efs/inode.c b/fs/efs/inode.c index 75903be12..0206ae6d5 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -10,6 +10,20 @@ #include <linux/efs_fs.h> #include <linux/efs_fs_sb.h> +extern int efs_get_block(struct inode *, long, struct buffer_head *, int); +static int efs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,efs_get_block); +} +static int _efs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,efs_get_block); +} +struct address_space_operations efs_aops = { + readpage: efs_readpage, + bmap: _efs_bmap +}; + static inline void extent_copy(efs_extent *src, efs_extent *dst) { /* * this is slightly evil. it doesn't just copy @@ -126,9 +140,11 @@ void efs_read_inode(struct inode *inode) { break; case S_IFREG: inode->i_op = &efs_file_inode_operations; + inode->i_data.a_ops = &efs_aops; break; case S_IFLNK: - inode->i_op = &efs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &efs_symlink_aops; break; case S_IFCHR: case S_IFBLK: diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c index 6bfc16be7..66776c7e5 100644 --- a/fs/efs/symlink.c +++ b/fs/efs/symlink.c @@ -48,8 +48,6 @@ fail: return err; } -struct inode_operations efs_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, +struct address_space_operations efs_symlink_aops = { readpage: efs_symlink_readpage }; @@ -265,7 +265,7 @@ int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm) * This routine is used to map in a page into an address space: needed by * execve() for the initial stack and environment pages. */ -static void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address) +void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address) { pgd_t * pgd; pmd_t * pmd; diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index c97292620..1d45d3514 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -31,18 +31,10 @@ static ssize_t ext2_dir_read (struct file * filp, char * buf, static int ext2_readdir(struct file *, void *, filldir_t); static struct file_operations ext2_dir_operations = { - NULL, /* lseek - default */ - ext2_dir_read, /* read */ - NULL, /* write - bad */ - ext2_readdir, /* readdir */ - NULL, /* poll - default */ - ext2_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - ext2_sync_file, /* fsync */ - NULL, /* fasync */ + read: ext2_dir_read, + readdir: ext2_readdir, + ioctl: ext2_ioctl, + fsync: ext2_sync_file, }; /* @@ -59,14 +51,6 @@ struct inode_operations ext2_dir_inode_operations = { ext2_rmdir, /* rmdir */ ext2_mknod, /* mknod */ ext2_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; int ext2_check_dir_entry (const char * function, struct inode * dir, diff --git a/fs/ext2/file.c b/fs/ext2/file.c index df010f223..5c4639175 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -21,13 +21,6 @@ #include <linux/fs.h> #include <linux/sched.h> - - -#define NBUF 32 - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - static loff_t ext2_file_lseek(struct file *, loff_t, int); static int ext2_open_file (struct inode *, struct file *); @@ -73,40 +66,6 @@ static loff_t ext2_file_lseek( return offset; } -static inline void remove_suid(struct inode *inode) -{ - unsigned int mode; - - /* set S_IGID if S_IXGRP is set, and always set S_ISUID */ - mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID; - - /* was any of the uid bits set? */ - mode &= inode->i_mode; - if (mode && !capable(CAP_FSETID)) { - inode->i_mode &= ~mode; - mark_inode_dirty(inode); - } -} - -/* - * Write to a file (through the page cache). - */ -static ssize_t -ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - ssize_t retval; - - retval = generic_file_write(file, buf, count, - ppos, block_write_partial_page); - if (retval > 0) { - struct inode *inode = file->f_dentry->d_inode; - remove_suid(inode); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - mark_inode_dirty(inode); - } - return retval; -} - /* * Called when an inode is released. Note that this is different * from ext2_file_open: open gets called at every open, but release @@ -137,37 +96,17 @@ static int ext2_open_file (struct inode * inode, struct file * filp) * the ext2 filesystem. */ static struct file_operations ext2_file_operations = { - ext2_file_lseek, /* lseek */ - generic_file_read, /* read */ - ext2_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - ext2_ioctl, /* ioctl */ - generic_file_mmap, /* mmap */ - ext2_open_file, - NULL, /* flush */ - ext2_release_file, /* release */ - ext2_sync_file, /* fsync */ - NULL, /* fasync */ + llseek: ext2_file_lseek, + read: generic_file_read, + write: generic_file_write, + ioctl: ext2_ioctl, + mmap: generic_file_mmap, + open: ext2_open_file, + release: ext2_release_file, + fsync: ext2_sync_file, }; struct inode_operations ext2_file_inode_operations = { - &ext2_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - ext2_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ - ext2_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ + &ext2_file_operations, + truncate: ext2_truncate, }; diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index c29fef5ea..52ffd6138 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -24,8 +24,7 @@ #include <linux/fs.h> #include <linux/locks.h> - - +#include <linux/smp_lock.h> #define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) @@ -130,6 +129,7 @@ int ext2_sync_file(struct file * file, struct dentry *dentry) int wait, err = 0; struct inode *inode = dentry->d_inode; + lock_kernel(); if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) /* * Don't sync fast links! @@ -152,5 +152,6 @@ int ext2_sync_file(struct file * file, struct dentry *dentry) } skip: err |= ext2_sync_inode (inode); + unlock_kernel(); return err ? -EIO : 0; } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index e92e4080a..ab7ac0a96 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -408,7 +408,7 @@ out: return result; } -int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) +static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { int ret, err, new; struct buffer_head *bh; @@ -610,6 +610,30 @@ struct buffer_head * ext2_bread (struct inode * inode, int block, return NULL; } +static int ext2_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,ext2_get_block); +} +static int ext2_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,ext2_get_block); +} +static int ext2_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,ext2_get_block); +} +static int ext2_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,ext2_get_block); +} +struct address_space_operations ext2_aops = { + readpage: ext2_readpage, + writepage: ext2_writepage, + prepare_write: ext2_prepare_write, + commit_write: generic_commit_write, + bmap: ext2_bmap +}; + void ext2_read_inode (struct inode * inode) { struct buffer_head * bh; @@ -719,15 +743,19 @@ void ext2_read_inode (struct inode * inode) if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) /* Nothing to do */ ; - else if (S_ISREG(inode->i_mode)) + else if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_mapping->a_ops = &ext2_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &ext2_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = inode->i_blocks - ?&ext2_symlink_inode_operations - :&ext2_fast_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + if (!inode->i_blocks) + inode->i_op = &ext2_fast_symlink_inode_operations; + else { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ext2_aops; + } + } else init_special_inode(inode, inode->i_mode, le32_to_cpu(raw_inode->i_block[0])); brelse (bh); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 08136962a..c2e5630ca 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -384,6 +384,7 @@ int ext2_create (struct inode * dir, struct dentry * dentry, int mode) return err; inode->i_op = &ext2_file_inode_operations; + inode->i_mapping->a_ops = &ext2_aops; inode->i_mode = mode; mark_inode_dirty(inode); bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); @@ -696,7 +697,8 @@ int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symnam inode->i_mode = S_IFLNK | S_IRWXUGO; if (l > sizeof (inode->u.ext2_i.i_data)) { - inode->i_op = &ext2_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ext2_aops; err = block_symlink(inode, symname, l); if (err) goto out_no_entry; diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 18fbbb368..d56ef4c62 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -1,6 +1,8 @@ /* * linux/fs/ext2/symlink.c * + * Only fast symlinks left here - the rest is done by generic code. AV, 1999 + * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal @@ -16,7 +18,6 @@ */ #include <linux/fs.h> -#include <linux/ext2_fs.h> static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen) { @@ -34,10 +35,3 @@ struct inode_operations ext2_fast_symlink_inode_operations = { readlink: ext2_readlink, follow_link: ext2_follow_link, }; - -struct inode_operations ext2_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - get_block: ext2_get_block, - readpage: block_read_full_page, -}; diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 45f4a800c..175182959 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -283,7 +283,7 @@ int default_fat_bmap(struct inode *inode,int sector) return 0; return sector+sb->dir_start; } - if (sector >= (MSDOS_I(inode)->i_realsize>>9)) + if (sector >= MSDOS_I(inode)->mmu_private>>9) return 0; cluster = sector/sb->cluster_size; offset = sector % sb->cluster_size; diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 83565fcbe..804876270 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -41,17 +41,10 @@ static ssize_t fat_dir_read(struct file * filp, char * buf, } struct file_operations fat_dir_operations = { - NULL, /* lseek - default */ - fat_dir_read, /* read */ - NULL, /* write - bad */ - fat_readdir, /* readdir */ - NULL, /* select v2.0.x/poll v2.1.x - default */ - fat_dir_ioctl, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync /* fsync */ + read: fat_dir_read, + readdir: fat_readdir, + ioctl: fat_dir_ioctl, + fsync: file_fsync, }; /* diff --git a/fs/fat/file.c b/fs/fat/file.c index 06da340fb..0c535b8da 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -31,17 +31,10 @@ #define Printk(x) printk x static struct file_operations fat_file_operations = { - NULL, /* lseek - default */ - fat_file_read, /* read */ - fat_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select v2.0.x/poll v2.1.x - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - file_fsync /* fsync */ + read: fat_file_read, + write: fat_file_write, + mmap: generic_file_mmap, + fsync: file_fsync, }; struct inode_operations fat_file_inode_operations = { @@ -57,12 +50,7 @@ struct inode_operations fat_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - fat_get_block, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ fat_truncate, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; ssize_t fat_file_read( @@ -88,7 +76,7 @@ int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_resul } if (!create) return 0; - if (iblock<<9 != MSDOS_I(inode)->i_realsize) { + if (iblock<<9 != MSDOS_I(inode)->mmu_private) { BUG(); return -EIO; } @@ -96,7 +84,7 @@ int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_resul if (fat_add_cluster(inode)) return -ENOSPC; } - MSDOS_I(inode)->i_realsize+=SECTOR_SIZE; + MSDOS_I(inode)->mmu_private += 512; phys=fat_bmap(inode, iblock); if (!phys) BUG(); @@ -107,30 +95,6 @@ int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_resul return 0; } -static int fat_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) -{ - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - struct page *new_page; - unsigned long pgpos; - long status; - - while((pgpos=MSDOS_I(inode)->i_realsize>>PAGE_CACHE_SHIFT)<page->index){ - status = -ENOMEM; - new_page = grab_cache_page(&inode->i_data, pgpos); - if (!new_page) - goto out; - status = block_write_cont_page(file, new_page, PAGE_SIZE, 0, NULL); - UnlockPage(new_page); - page_cache_release(new_page); - if (status < 0) - goto out; - } - status = block_write_cont_page(file, page, offset, bytes, buf); -out: - return status; -} - ssize_t fat_file_write( struct file *filp, const char *buf, @@ -152,8 +116,7 @@ ssize_t default_fat_file_write( struct inode *inode = filp->f_dentry->d_inode; int retval; - retval = generic_file_write(filp, buf, count, ppos, - fat_write_partial_page); + retval = generic_file_write(filp, buf, count, ppos); if (retval > 0) { inode->i_mtime = inode->i_ctime = CURRENT_TIME; MSDOS_I(inode)->i_attrs |= ATTR_ARCH; @@ -173,7 +136,7 @@ void fat_truncate(struct inode *inode) if (IS_IMMUTABLE(inode)) return /* -EPERM */; cluster = SECTOR_SIZE*sbi->cluster_size; - MSDOS_I(inode)->i_realsize = ((inode->i_size-1) | (SECTOR_SIZE-1)) + 1; + MSDOS_I(inode)->mmu_private = inode->i_size; fat_free(inode,(inode->i_size+(cluster-1))>>sbi->cluster_bits); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; inode->i_ctime = inode->i_mtime = CURRENT_TIME; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 3919f6517..90b288832 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -397,7 +397,7 @@ static void fat_read_root(struct inode *inode) ((inode->i_size+inode->i_blksize-1)>>sbi->cluster_bits) * sbi->cluster_size; MSDOS_I(inode)->i_logstart = 0; - MSDOS_I(inode)->i_realsize = inode->i_size; + MSDOS_I(inode)->mmu_private = inode->i_size; MSDOS_I(inode)->i_attrs = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = 0; @@ -746,6 +746,31 @@ static int is_exec(char *extension) return 0; } +static int fat_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,fat_get_block); +} +static int fat_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,fat_get_block); +} +static int fat_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,fat_get_block, + &MSDOS_I((struct inode*)page->mapping->host)->mmu_private); +} +static int _fat_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,fat_get_block); +} +static struct address_space_operations fat_aops = { + readpage: fat_readpage, + writepage: fat_writepage, + prepare_write: fat_prepare_write, + commit_write: generic_commit_write, + bmap: _fat_bmap +}; + /* doesn't deal with root inode */ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) { @@ -788,7 +813,7 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) break; } } - MSDOS_I(inode)->i_realsize = inode->i_size; + MSDOS_I(inode)->mmu_private = inode->i_size; } else { /* not a directory */ inode->i_mode = MSDOS_MKMODE(de->attr, ((IS_NOEXEC(inode) || @@ -796,7 +821,6 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) !is_exec(de->ext))) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~sbi->options.fs_umask) | S_IFREG; - inode->i_op = &fat_file_inode_operations; MSDOS_I(inode)->i_start = CF_LE_W(de->start); if (sbi->fat_bits == 32) { MSDOS_I(inode)->i_start |= @@ -805,7 +829,9 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start; inode->i_nlink = 1; inode->i_size = CF_LE_L(de->size); - MSDOS_I(inode)->i_realsize = ((inode->i_size-1)|(SECTOR_SIZE-1))+1; + inode->i_op = &fat_file_inode_operations; + inode->i_mapping->a_ops = &fat_aops; + MSDOS_I(inode)->mmu_private = inode->i_size; } if(de->attr & ATTR_SYS) if (sbi->options.sys_immutable) diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 196e7aa1b..bca75467b 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -311,7 +311,7 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); ~(SECTOR_SIZE-1); } inode->i_size += SECTOR_SIZE*cluster_size; - MSDOS_I(inode)->i_realsize += SECTOR_SIZE*cluster_size; + MSDOS_I(inode)->mmu_private += SECTOR_SIZE*cluster_size; mark_inode_dirty(inode); return res; } @@ -153,7 +153,7 @@ err_nolock_nocleanup: * depending on the access mode of the file... */ static struct file_operations def_fifo_fops = { - open: fifo_open, /* will set read or write pipe_fops */ + open: fifo_open, /* will set read or write pipe_fops */ }; struct inode_operations fifo_inode_operations = { diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index cdf01737a..e0dab7583 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -58,18 +58,9 @@ const struct hfs_name hfs_cap_reserved2[] = { #define DOT_ROOTINFO (&hfs_cap_reserved2[0]) static struct file_operations hfs_cap_dir_operations = { - NULL, /* lseek - default */ - hfs_dir_read, /* read - invalid */ - NULL, /* write - bad */ - cap_readdir, /* readdir */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - NULL, /* mmap - none */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync, /* fsync - default */ - NULL, /* fasync - default */ + read: hfs_dir_read, + readdir: cap_readdir, + fsync: file_fsync, }; struct inode_operations hfs_cap_ndir_inode_operations = { @@ -83,56 +74,18 @@ struct inode_operations hfs_cap_ndir_inode_operations = { hfs_rmdir, /* rmdir */ NULL, /* mknod */ hfs_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; struct inode_operations hfs_cap_fdir_inode_operations = { &hfs_cap_dir_operations,/* default directory file-ops */ NULL, /* create */ cap_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; struct inode_operations hfs_cap_rdir_inode_operations = { &hfs_cap_dir_operations,/* default directory file-ops */ hfs_create, /* create */ cap_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; /*================ File-local functions ================*/ diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 1e13465d1..f3469b402 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -57,18 +57,9 @@ const struct hfs_name hfs_dbl_reserved2[] = { #define PCNT_ROOTINFO (&hfs_dbl_reserved2[1]) static struct file_operations hfs_dbl_dir_operations = { - NULL, /* lseek - default */ - hfs_dir_read, /* read - invalid */ - NULL, /* write - bad */ - dbl_readdir, /* readdir */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - NULL, /* mmap - none */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync, /* fsync - default */ - NULL, /* fasync - default */ + read: hfs_dir_read, + readdir: dbl_readdir, + fsync: file_fsync, }; struct inode_operations hfs_dbl_dir_inode_operations = { @@ -82,14 +73,6 @@ struct inode_operations hfs_dbl_dir_inode_operations = { dbl_rmdir, /* rmdir */ NULL, /* mknod */ dbl_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index ab274d18f..05a646384 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -63,19 +63,9 @@ const struct hfs_name hfs_nat_reserved2[] = { #define ROOTINFO (&hfs_nat_reserved2[0]) static struct file_operations hfs_nat_dir_operations = { - NULL, /* lseek - default */ - hfs_dir_read, /* read - invalid */ - NULL, /* write - bad */ - nat_readdir, /* readdir */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - NULL, /* mmap - none */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync, /* fsync - default */ - NULL, /* fasync - default */ - NULL /* lock - none */ + read: hfs_dir_read, + readdir: nat_readdir, + fsync: file_fsync, }; struct inode_operations hfs_nat_ndir_inode_operations = { @@ -89,14 +79,6 @@ struct inode_operations hfs_nat_ndir_inode_operations = { nat_rmdir, /* rmdir */ NULL, /* mknod */ hfs_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; struct inode_operations hfs_nat_hdir_inode_operations = { @@ -110,14 +92,6 @@ struct inode_operations hfs_nat_hdir_inode_operations = { NULL, /* rmdir */ NULL, /* mknod */ nat_hdr_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; /*================ File-local functions ================*/ diff --git a/fs/hfs/file.c b/fs/hfs/file.c index d5edeac45..bb0868fca 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -28,24 +28,14 @@ static hfs_rwret_t hfs_file_read(struct file *, char *, hfs_rwarg_t, static hfs_rwret_t hfs_file_write(struct file *, const char *, hfs_rwarg_t, loff_t *); static void hfs_file_truncate(struct inode *); -static int hfs_get_block(struct inode *, long, struct buffer_head *, int); /*================ Global variables ================*/ static struct file_operations hfs_file_operations = { - NULL, /* lseek - default */ - hfs_file_read, /* read */ - hfs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - file_fsync, /* fsync - default */ - NULL, /* fasync - default */ - NULL /* lock - none */ + read: hfs_file_read, + write: hfs_file_write, + mmap: generic_file_mmap, + fsync: file_fsync, }; struct inode_operations hfs_file_inode_operations = { @@ -61,12 +51,7 @@ struct inode_operations hfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - hfs_get_block, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ hfs_file_truncate, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; /*================ Variable-like macros ================*/ diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c index 4e723af63..77625077f 100644 --- a/fs/hfs/file_cap.c +++ b/fs/hfs/file_cap.c @@ -47,19 +47,9 @@ static void cap_info_truncate(struct inode *); /*================ Global variables ================*/ static struct file_operations hfs_cap_info_operations = { - NULL, /* lseek - default */ - cap_info_read, /* read */ - cap_info_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - NULL, /* mmap - not yet */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync, /* fsync - default */ - NULL, /* fasync - default */ - NULL /* lock - none */ + read: cap_info_read, + write: cap_info_write, + fsync: file_fsync, }; struct inode_operations hfs_cap_info_inode_operations = { @@ -75,12 +65,7 @@ struct inode_operations hfs_cap_info_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block - none */ - NULL, /* readpage */ - NULL, /* writepage */ cap_info_truncate, /* truncate */ - NULL, /* permission */ - NULL /* revalidata */ }; /*================ File-local functions ================*/ diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index efbe84f75..b0cf0291e 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -48,19 +48,9 @@ static void hdr_truncate(struct inode *); /*================ Global variables ================*/ static struct file_operations hfs_hdr_operations = { - NULL, /* lseek - default */ - hdr_read, /* read */ - hdr_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - NULL, /* mmap - XXX: not yet */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync, /* fsync - default */ - NULL, /* fasync - default */ - NULL /* lock - none */ + read: hdr_read, + write: hdr_write, + fsync: file_fsync, }; struct inode_operations hfs_hdr_inode_operations = { @@ -76,13 +66,7 @@ struct inode_operations hfs_hdr_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block - XXX: not available since - header part has no disk block */ - NULL, /* readpage */ - NULL, /* writepage */ hdr_truncate, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = { diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index 07e61f0de..3819a685f 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -494,6 +494,9 @@ extern int hfs_extent_map(struct hfs_fork *, int, int); extern void hfs_extent_adj(struct hfs_fork *); extern void hfs_extent_free(struct hfs_fork *); +/* file.c */ +extern int hfs_get_block(struct inode *, long, struct buffer_head *, int); + /* mdb.c */ extern struct hfs_mdb *hfs_mdb_get(hfs_sysmdb, int, hfs_s32); extern void hfs_mdb_commit(struct hfs_mdb *, int); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index af09655a0..60014426b 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -184,6 +184,31 @@ int hfs_notify_change(struct dentry *dentry, struct iattr * attr) return 0; } +static int hfs_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,hfs_get_block); +} +static int hfs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,hfs_get_block); +} +static int hfs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,hfs_get_block, + &((struct inode*)page->mapping->host)->u.hfs_i.mmu_private); +} +static int hfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,hfs_get_block); +} +struct address_space_operations hfs_aops = { + readpage: hfs_readpage, + writepage: hfs_writepage, + prepare_write: hfs_prepare_write, + commit_write: generic_commit_write, + bmap: hfs_bmap +}; + /* * __hfs_iget() * @@ -308,6 +333,8 @@ void hfs_cap_ifill(struct inode * inode, ino_t type, const int version) init_file_inode(inode, (type == HFS_CAP_DATA) ? HFS_FK_DATA : HFS_FK_RSRC); inode->i_op = &hfs_file_inode_operations; + inode->i_mapping->a_ops = &hfs_aops; + inode->u.hfs_i.mmu_private = inode->i_size; } else { /* Directory */ struct hfs_dir *hdir = &entry->u.dir; @@ -361,6 +388,8 @@ void hfs_dbl_ifill(struct inode * inode, ino_t type, const int version) } else if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_DATA); inode->i_op = &hfs_file_inode_operations; + inode->i_mapping->a_ops = &hfs_aops; + inode->u.hfs_i.mmu_private = inode->i_size; } else { /* Directory */ struct hfs_dir *hdir = &entry->u.dir; @@ -402,6 +431,8 @@ void hfs_nat_ifill(struct inode * inode, ino_t type, const int version) } else if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_DATA); inode->i_op = &hfs_file_inode_operations; + inode->i_mapping->a_ops = &hfs_aops; + inode->u.hfs_i.mmu_private = inode->i_size; } else { /* Directory */ struct hfs_dir *hdir = &entry->u.dir; diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 36e665c32..280c47244 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -8,7 +8,7 @@ #include "hpfs_fn.h" -int hpfs_dir_read(struct file *filp, char *name, size_t len, loff_t *loff) +ssize_t hpfs_dir_read(struct file *filp, char *name, size_t len, loff_t *loff) { return -EISDIR; } @@ -242,6 +242,8 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry) if (!de->directory) { if (result->i_size == -1) { result->i_size = de->file_size; + result->i_data.a_ops = &hpfs_aops; + result->u.hpfs_i.mmu_private = result->i_size; /* * i_blocks should count the fnode and any anodes. * We count 1 for the fnode and don't bother about diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 8abaf2fd3..710b9120b 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -54,6 +54,7 @@ void hpfs_truncate(struct inode *i) if (IS_IMMUTABLE(i)) return /*-EPERM*/; i->i_hpfs_n_secs = 0; i->i_blocks = 1 + ((i->i_size + 511) >> 9); + i->u.hpfs_i.mmu_private = i->i_size; hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); hpfs_write_inode(i); } @@ -61,63 +62,60 @@ void hpfs_truncate(struct inode *i) int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { secno s; - if (iblock < inode->i_blocks - 1) { - s = hpfs_bmap(inode, iblock); + s = hpfs_bmap(inode, iblock); + if (s) { bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = s; bh_result->b_state |= (1UL << BH_Mapped); return 0; } if (!create) return 0; - if (iblock > inode->i_blocks - 1) { - //hpfs_error(inode->i_sb, "hpfs_get_block beyond file end (requested %08x, inode size %08x", (int)iblock, (int)inode->i_blocks - 1); - printk("HPFS: could not write beyond file end. This is known bug.\n"); - return -EFSERROR; + if (iblock<<9 != inode->u.hpfs_i.mmu_private) { + BUG(); + return -EIO; } if ((s = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1)) == -1) { hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1); return -ENOSPC; } inode->i_blocks++; + inode->u.hpfs_i.mmu_private += 512; bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = s; bh_result->b_state |= (1UL << BH_Mapped) | (1UL << BH_New); return 0; } -static int hpfs_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +static int hpfs_writepage(struct dentry *dentry, struct page *page) { - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - struct page *new_page; - unsigned long pgpos; - long status; - - pgpos = ((inode->i_blocks - 1) * 512) >> PAGE_CACHE_SHIFT; - while (pgpos < page->index) { - status = -ENOMEM; - new_page = grab_cache_page(&inode->i_data, pgpos); - if (!new_page) - goto out; - status = block_write_cont_page(file, new_page, PAGE_SIZE, 0, NULL); - UnlockPage(new_page); - page_cache_release(new_page); - if (status < 0) - goto out; - pgpos = ((inode->i_blocks - 1) * 512) >> PAGE_CACHE_SHIFT; - } - status = block_write_cont_page(file, page, offset, bytes, buf); -out: - return status; + return block_write_full_page(page,hpfs_get_block); } - +static int hpfs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,hpfs_get_block); +} +static int hpfs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,hpfs_get_block, + &((struct inode*)page->mapping->host)->u.hpfs_i.mmu_private); +} +static int _hpfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,hpfs_get_block); +} +struct address_space_operations hpfs_aops = { + readpage: hpfs_readpage, + writepage: hpfs_writepage, + prepare_write: hpfs_prepare_write, + commit_write: generic_commit_write, + bmap: _hpfs_bmap +}; ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { ssize_t retval; - retval = generic_file_write(file, buf, count, - ppos, hpfs_write_partial_page); + retval = generic_file_write(file, buf, count, ppos); if (retval > 0) { struct inode *inode = file->f_dentry->d_inode; inode->i_mtime = CURRENT_TIME; diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index d1e70e579..f0324cffa 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -224,7 +224,7 @@ void hpfs_set_dentry_operations(struct dentry *); /* dir.c */ -int hpfs_dir_read(struct file *, char *, size_t, loff_t *); +ssize_t hpfs_dir_read(struct file *, char *, size_t, loff_t *); int hpfs_dir_release(struct inode *, struct file *); loff_t hpfs_dir_lseek(struct file *, loff_t, int); int hpfs_readdir(struct file *, void *, filldir_t); @@ -314,3 +314,5 @@ void hpfs_put_super(struct super_block *); unsigned hpfs_count_one_bitmap(struct super_block *, secno); int hpfs_statfs(struct super_block *, struct statfs *, int); struct super_block *hpfs_read_super(struct super_block *, void *, int); + +extern struct address_space_operations hpfs_aops; diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 0b6ab8053..2fa2d0dd4 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -10,19 +10,12 @@ static const struct file_operations hpfs_file_ops = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - hpfs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - hpfs_open, /* open */ - NULL, /* flush */ - hpfs_file_release, /* release */ - hpfs_file_fsync, /* fsync */ - NULL, /* fasync */ - NULL, /* lock */ + read: generic_file_read, + write: hpfs_file_write, + mmap: generic_file_mmap, + open: hpfs_open, + release: hpfs_file_release, + fsync: hpfs_file_fsync, }; static const struct inode_operations hpfs_file_iops = @@ -39,29 +32,17 @@ static const struct inode_operations hpfs_file_iops = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - &hpfs_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ hpfs_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ }; static const struct file_operations hpfs_dir_ops = { - hpfs_dir_lseek, /* lseek */ - hpfs_dir_read, /* read */ - NULL, /* write - bad */ - hpfs_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - hpfs_open, /* open */ - NULL, /* flush */ - hpfs_dir_release, /* no special release code */ - hpfs_file_fsync, /* fsync */ - NULL, /* fasync */ - NULL, /* lock */ + llseek: hpfs_dir_lseek, + read: hpfs_dir_read, + readdir: hpfs_readdir, + open: hpfs_open, + release: hpfs_dir_release, + fsync: hpfs_file_fsync, }; static const struct inode_operations hpfs_dir_iops = @@ -76,20 +57,9 @@ static const struct inode_operations hpfs_dir_iops = hpfs_rmdir, /* rmdir */ hpfs_mknod, /* mknod */ hpfs_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; -const struct inode_operations hpfs_symlink_iops = -{ - readlink: page_readlink, - follow_link: page_follow_link, +struct address_space_operations hpfs_symlink_aops = { readpage: hpfs_symlink_readpage }; @@ -164,7 +134,8 @@ void hpfs_read_inode(struct inode *i) if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) { kfree(ea); i->i_mode = S_IFLNK | 0777; - i->i_op = (struct inode_operations *) &hpfs_symlink_iops; + i->i_op = &page_symlink_inode_operations; + i->i_data.a_ops = &hpfs_symlink_aops; i->i_nlink = 1; i->i_size = ea_size; i->i_blocks = 1; @@ -219,6 +190,8 @@ void hpfs_read_inode(struct inode *i) i->i_nlink = 1; i->i_size = fnode->file_size; i->i_blocks = ((i->i_size + 511) >> 9) + 1; + i->i_data.a_ops = &hpfs_aops; + i->u.hpfs_i.mmu_private = i->i_size; } brelse(bh); } diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index 85ce143db..3ca25fedf 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -138,7 +138,11 @@ int hpfs_create(struct inode *dir, struct dentry *dentry, int mode) result->i_hpfs_ea_size = 0; if (dee.read_only) result->i_mode &= ~0222; if (result->i_blocks == -1) result->i_blocks = 1; - if (result->i_size == -1) result->i_size = 0; + if (result->i_size == -1) { + result->i_size = 0; + result->i_data.a_ops = &hpfs_aops; + result->u.hpfs_i.mmu_private = 0; + } if (result->i_uid != current->fsuid || result->i_gid != current->fsgid || result->i_mode != (mode | S_IFREG)) { @@ -221,7 +225,7 @@ int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) return -ENOSPC; } -extern const struct inode_operations hpfs_symlink_iops; +extern struct address_space_operations hpfs_symlink_aops; int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink) { @@ -268,7 +272,8 @@ int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink) result->i_gid = current->fsgid; result->i_blocks = 1; result->i_size = strlen(symlink); - result->i_op = (struct inode_operations *) &hpfs_symlink_iops; + result->i_op = &page_symlink_inode_operations; + result->i_data.a_ops = &hpfs_symlink_aops; if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) { hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink)); mark_buffer_dirty(bh, 1); diff --git a/fs/inode.c b/fs/inode.c index 4145a2d91..ccc1a62f3 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -474,6 +474,8 @@ static void clean_inode(struct inode *inode) memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); inode->i_pipe = NULL; inode->i_bdev = NULL; + inode->i_mapping = &inode->i_data; + inode->i_mapping->host = (void*)inode; } /* @@ -719,15 +721,10 @@ kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); int bmap(struct inode * inode, int block) { - struct buffer_head tmp; - - if (inode->i_op && inode->i_op->get_block) { - tmp.b_state = 0; - tmp.b_blocknr = 0; - inode->i_op->get_block(inode, block, &tmp, 0); - return tmp.b_blocknr; - } - return 0; + int res = 0; + if (inode->i_mapping->a_ops->bmap) + res = inode->i_mapping->a_ops->bmap(inode->i_mapping, block); + return res; } /* diff --git a/fs/ioctl.c b/fs/ioctl.c index e7e226056..a02bbec67 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -19,21 +19,18 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) switch (cmd) { case FIBMAP: { - struct buffer_head tmp; - - if (inode->i_op == NULL) - return -EBADF; - if (inode->i_op->get_block == NULL) + struct address_space *mapping = inode->i_mapping; + int res; + /* do we support this mess? */ + if (!mapping->a_ops->bmap) return -EINVAL; if (!capable(CAP_SYS_RAWIO)) return -EPERM; if ((error = get_user(block, (int *) arg)) != 0) return error; - tmp.b_state = 0; - tmp.b_blocknr = 0; - inode->i_op->get_block(inode, block, &tmp, 0); - return put_user(tmp.b_blocknr, (int *) arg); + res = mapping->a_ops->bmap(mapping, block); + return put_user(res, (int *) arg); } case FIGETBSZ: if (inode->i_sb == NULL) diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile index 11c340284..2b2af2ade 100644 --- a/fs/isofs/Makefile +++ b/fs/isofs/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := isofs.o -O_OBJS := namei.o inode.o file.o dir.o util.o rock.o symlink.o +O_OBJS := namei.o inode.o file.o dir.o util.o rock.o ifdef CONFIG_JOLIET O_OBJS += joliet.o diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index fb60d3a55..35f37a1bf 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -28,16 +28,7 @@ static int isofs_readdir(struct file *, void *, filldir_t); static struct file_operations isofs_dir_operations = { - NULL, /* lseek - default */ - NULL, /* read */ - NULL, /* write - bad */ - isofs_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* fsync */ + readdir: isofs_readdir, }; /* @@ -48,21 +39,6 @@ struct inode_operations isofs_dir_inode_operations = &isofs_dir_operations, /* default directory file-ops */ NULL, /* create */ isofs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static int isofs_name_translate(char * old, int len, char * new) diff --git a/fs/isofs/file.c b/fs/isofs/file.c index b15b6c5dc..9de31e024 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -22,36 +22,10 @@ * the isofs filesystem. */ static struct file_operations isofs_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - NULL, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - NULL /* fsync */ + read: generic_file_read, + mmap: generic_file_mmap, }; struct inode_operations isofs_file_inode_operations = { &isofs_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - isofs_get_block, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d14c18071..2c67d9e5a 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1006,6 +1006,19 @@ int isofs_bmap(struct inode *inode, int block) return 0; } +static int isofs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,isofs_get_block); +} +static int _isofs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,isofs_get_block); +} +static struct address_space_operations isofs_aops = { + readpage: isofs_readpage, + bmap: _isofs_bmap +}; + static void test_and_set_uid(uid_t *p, uid_t value) { if(value) { @@ -1254,13 +1267,15 @@ static void isofs_read_inode(struct inode * inode) } else #endif IGNORE_WRONG_MULTI_VOLUME_SPECS { - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &isofs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_data.a_ops = &isofs_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &isofs_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &isofs_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &isofs_symlink_aops; + } else /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ init_special_inode(inode, inode->i_mode, kdev_t_to_nr(inode->i_rdev)); } diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 814d90b3c..71be5edd1 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -445,7 +445,7 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr) /* readpage() for symlinks: reads symlink contents into the page and either makes it uptodate and returns 0 or returns error (-EIO) */ -int rock_ridge_symlink_readpage(struct dentry *dentry, struct page *page) +static int rock_ridge_symlink_readpage(struct dentry *dentry, struct page *page) { struct inode *inode = dentry->d_inode; char *link = (char*)kmap(page); @@ -540,3 +540,7 @@ int rock_ridge_symlink_readpage(struct dentry *dentry, struct page *page) UnlockPage(page); return -EIO; } + +struct address_space_operations isofs_symlink_aops = { + readpage: rock_ridge_symlink_readpage +}; diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c deleted file mode 100644 index 219a4b9ac..000000000 --- a/fs/isofs/symlink.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * linux/fs/isofs/symlink.c - * - * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * isofs symlink handling code. This is only used with the Rock Ridge - * extensions to iso9660 - */ - -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/fs.h> -#include <linux/iso_fs.h> -#include <linux/stat.h> -#include <linux/malloc.h> - -/* - * symlinks can't do much... - */ -struct inode_operations isofs_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - readpage: rock_ridge_symlink_readpage -}; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 878797b8a..e22007445 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -308,13 +308,13 @@ out: #ifdef MODULE /* New module support in 2.1.18 */ -#if LINUX_VERSION_CODE >= 0x020112 - EXPORT_NO_SYMBOLS; - MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); - MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION "."); - MODULE_PARM(nlm_grace_period, "10-240l"); - MODULE_PARM(nlm_timeout, "3-20l"); -#endif + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); +MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION "."); +MODULE_PARM(nlm_grace_period, "10-240l"); +MODULE_PARM(nlm_timeout, "3-20l"); + int init_module(void) { diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index cb090cfd3..0902eaa0d 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -95,8 +95,8 @@ nlm_decode_fh(u32 *p, struct nfs_fh *f) if ((len = ntohl(*p++)) != sizeof(*f)) { printk(KERN_NOTICE - "lockd: bad fhandle size %u (should be %lu)\n", - len, (unsigned long) sizeof(*f)); + "lockd: bad fhandle size %x (should be %Zu)\n", + len, sizeof(*f)); return NULL; } memcpy(f, p, sizeof(*f)); diff --git a/fs/minix/Makefile b/fs/minix/Makefile index 537127b2d..c3aee4fe3 100644 --- a/fs/minix/Makefile +++ b/fs/minix/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := minix.o -O_OBJS := bitmap.o truncate.o namei.o inode.o file.o dir.o symlink.o fsync.o +O_OBJS := bitmap.o truncate.o namei.o inode.o file.o dir.o fsync.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 52c0e0cee..02bde162d 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -23,17 +23,9 @@ static ssize_t minix_dir_read(struct file * filp, char * buf, static int minix_readdir(struct file *, void *, filldir_t); static struct file_operations minix_dir_operations = { - NULL, /* lseek - default */ - minix_dir_read, /* read */ - NULL, /* write - bad */ - minix_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync /* default fsync */ + read: minix_dir_read, + readdir: minix_readdir, + fsync: file_fsync, }; /* @@ -50,14 +42,6 @@ struct inode_operations minix_dir_inode_operations = { minix_rmdir, /* rmdir */ minix_mknod, /* mknod */ minix_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static int minix_readdir(struct file * filp, diff --git a/fs/minix/file.c b/fs/minix/file.c index 6445c225c..61d0e3f02 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -18,60 +18,21 @@ #include <asm/uaccess.h> #include <asm/system.h> -#define NBUF 32 - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - #include <linux/fs.h> #include <linux/minix_fs.h> /* - * Write to a file (through the page cache). - */ -static ssize_t -minix_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - return generic_file_write(file, buf, count, - ppos, block_write_partial_page); -} - -/* * We have mostly NULLs here: the current defaults are OK for * the minix filesystem. */ static struct file_operations minix_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - minix_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - minix_sync_file, /* fsync */ - NULL, /* fasync */ + read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap, + fsync: minix_sync_file, }; struct inode_operations minix_file_inode_operations = { - &minix_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - minix_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ - minix_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ + &minix_file_operations, + truncate: minix_truncate, }; diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index 2fcdddf15..30794d27a 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -16,6 +16,7 @@ #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/locks.h> +#include <linux/smp_lock.h> #include <linux/fs.h> #include <linux/minix_fs.h> @@ -148,10 +149,7 @@ static int V1_minix_sync_file(struct inode * inode, struct file * file) { int wait, err = 0; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return -EINVAL; - + lock_kernel(); for (wait=0; wait<=1; wait++) { err |= V1_sync_direct(inode, wait); @@ -159,6 +157,7 @@ static int V1_minix_sync_file(struct inode * inode, struct file * file) err |= V1_sync_dindirect(inode, inode->u.minix_i.u.i1_data + 8, wait); } err |= minix_sync_inode (inode); + unlock_kernel(); return (err < 0) ? -EIO : 0; } @@ -309,10 +308,7 @@ static int V2_minix_sync_file(struct inode * inode, struct file * file) { int wait, err = 0; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return -EINVAL; - + lock_kernel(); for (wait=0; wait<=1; wait++) { err |= V2_sync_direct(inode, wait); @@ -324,6 +320,7 @@ static int V2_minix_sync_file(struct inode * inode, struct file * file) (unsigned long *) inode->u.minix_i.u.i2_data + 9, wait); } err |= minix_sync_inode (inode); + unlock_kernel(); return (err < 0) ? -EIO : 0; } @@ -336,6 +333,10 @@ int minix_sync_file(struct file * file, struct dentry *dentry) { struct inode *inode = dentry->d_inode; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; + if (INODE_VERSION(inode) == MINIX_V1) return V1_minix_sync_file(inode, file); else diff --git a/fs/minix/inode.c b/fs/minix/inode.c index de093d02f..e33fe9787 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -968,7 +968,7 @@ abort_too_big: goto abort; } -int minix_get_block(struct inode *inode, long block, +static int minix_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) { if (INODE_VERSION(inode) == MINIX_V1) @@ -1016,6 +1016,30 @@ struct buffer_head * minix_bread(struct inode * inode, int block, int create) return NULL; } +static int minix_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,minix_get_block); +} +static int minix_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,minix_get_block); +} +static int minix_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,minix_get_block); +} +static int minix_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,minix_get_block); +} +struct address_space_operations minix_aops = { + readpage: minix_readpage, + writepage: minix_writepage, + prepare_write: minix_prepare_write, + commit_write: generic_commit_write, + bmap: minix_bmap +}; + /* * The minix V1 function to read an inode. */ @@ -1053,13 +1077,15 @@ static void V1_minix_read_inode(struct inode * inode) inode->i_blocks = inode->i_blksize = 0; for (block = 0; block < 9; block++) inode->u.minix_i.u.i1_data[block] = raw_inode->i_zone[block]; - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &minix_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_mapping->a_ops = &minix_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &minix_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &minix_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &minix_aops; + } else init_special_inode(inode, inode->i_mode, raw_inode->i_zone[0]); brelse(bh); } @@ -1103,13 +1129,15 @@ static void V2_minix_read_inode(struct inode * inode) inode->i_blocks = inode->i_blksize = 0; for (block = 0; block < 10; block++) inode->u.minix_i.u.i2_data[block] = raw_inode->i_zone[block]; - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &minix_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_mapping->a_ops = &minix_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &minix_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &minix_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &minix_aops; + } else init_special_inode(inode, inode->i_mode, raw_inode->i_zone[0]); brelse(bh); } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index a3e73dbcd..de2d0f279 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -219,6 +219,7 @@ int minix_create(struct inode * dir, struct dentry *dentry, int mode) if (!inode) return -ENOSPC; inode->i_op = &minix_file_inode_operations; + inode->i_mapping->a_ops = &minix_aops; inode->i_mode = mode; mark_inode_dirty(inode); error = minix_add_entry(dir, dentry->d_name.name, @@ -477,7 +478,8 @@ int minix_symlink(struct inode * dir, struct dentry *dentry, goto out; inode->i_mode = S_IFLNK | 0777; - inode->i_op = &minix_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &minix_aops; err = block_symlink(inode, symname, i); if (err) goto fail; diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c deleted file mode 100644 index 7e1d03a10..000000000 --- a/fs/minix/symlink.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * linux/fs/minix/symlink.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * minix symlink handling code - * - * Code removed. 1999, AV ;-) - */ - -#include <linux/fs.h> -#include <linux/minix_fs.h> - -/* - * symlinks can't do much... - */ -struct inode_operations minix_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - get_block: minix_get_block, - readpage: block_read_full_page -}; diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index ea0b05616..155489072 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -595,14 +595,6 @@ struct inode_operations msdos_dir_inode_operations = { msdos_rmdir, /* rmdir */ NULL, /* mknod */ msdos_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ }; static void msdos_put_super_callback(struct super_block *sb) diff --git a/fs/namei.c b/fs/namei.c index 3b0b73686..102f0e432 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1386,8 +1386,8 @@ unsigned int follow, const char *link) static char *page_getlink(struct dentry * dentry, struct page **ppage) { struct page * page; - page = read_cache_page(&dentry->d_inode->i_data, 0, - (filler_t *)dentry->d_inode->i_op->readpage, + struct address_space *mapping = dentry->d_inode->i_mapping; + page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, dentry); if (IS_ERR(page)) goto sync_fail; @@ -1429,3 +1429,8 @@ page_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow } return res; } + +struct inode_operations page_symlink_inode_operations = { + readlink: page_readlink, + follow_link: page_follow_link, +}; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 565f88c5b..e5e91a24c 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -47,17 +47,9 @@ extern int ncp_symlink(struct inode *, struct dentry *, const char *); static struct file_operations ncp_dir_operations = { - NULL, /* lseek - default */ - ncp_dir_read, /* read - bad */ - NULL, /* write - bad */ - ncp_readdir, /* readdir */ - NULL, /* poll - default */ - ncp_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* fsync */ + read: ncp_dir_read, + readdir: ncp_readdir, + ioctl: ncp_ioctl, }; struct inode_operations ncp_dir_inode_operations = @@ -78,9 +70,6 @@ struct inode_operations ncp_dir_inode_operations = ncp_rename, /* rename */ NULL, /* readlink */ NULL, /* follow link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* revalidate */ diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 5e28516bf..09b95cd4f 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -277,17 +277,11 @@ out: static struct file_operations ncp_file_operations = { - NULL, /* lseek - default */ - ncp_file_read, /* read */ - ncp_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - ncp_ioctl, /* ioctl */ - ncp_mmap, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - ncp_fsync, /* fsync */ + read: ncp_file_read, + write: ncp_file_write, + ioctl: ncp_ioctl, + mmap: ncp_mmap, + fsync: ncp_fsync, }; struct inode_operations ncp_file_inode_operations = @@ -304,9 +298,6 @@ struct inode_operations ncp_file_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ NULL, /* permission */ NULL /* revalidate */ diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 84b9e5643..5a4e790a7 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -51,7 +51,7 @@ static struct super_operations ncp_sops = extern struct dentry_operations ncp_dentry_operations; #ifdef CONFIG_NCPFS_EXTRAS -extern struct inode_operations ncp_symlink_inode_operations; +extern struct address_space_operations ncp_symlink_aops; extern int ncp_symlink(struct inode*, struct dentry*, const char*); #endif @@ -226,7 +226,8 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) inode->i_op = &ncp_dir_inode_operations; #ifdef CONFIG_NCPFS_EXTRAS } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &ncp_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &ncp_symlink_aops; #endif } insert_inode_hash(inode); diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c index 5e5bf2fbd..b0bc34b22 100644 --- a/fs/ncpfs/symlink.c +++ b/fs/ncpfs/symlink.c @@ -96,9 +96,7 @@ fail: /* * symlinks can't do much... */ -struct inode_operations ncp_symlink_inode_operations={ - readlink: page_readlink, - follow_link: page_follow_link, +struct address_space_operations ncp_symlink_aops = { readpage: ncp_symlink_readpage, }; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 788307cc2..82d3f5bc3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -52,17 +52,10 @@ static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); static struct file_operations nfs_dir_operations = { - NULL, /* lseek - default */ - nfs_dir_read, /* read - bad */ - NULL, /* write - bad */ - nfs_readdir, /* readdir */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - nfs_open, /* open */ - NULL, /* flush */ - nfs_release, /* release */ - NULL /* fsync */ + read: nfs_dir_read, + readdir: nfs_readdir, + open: nfs_open, + release: nfs_release, }; struct inode_operations nfs_dir_inode_operations = { @@ -78,9 +71,6 @@ struct inode_operations nfs_dir_inode_operations = { nfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ NULL, /* permission */ nfs_revalidate, /* revalidate */ diff --git a/fs/nfs/file.c b/fs/nfs/file.c index f687cfdab..2135ece68 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -41,19 +41,14 @@ static int nfs_file_flush(struct file *); static int nfs_fsync(struct file *, struct dentry *dentry); static struct file_operations nfs_file_operations = { - NULL, /* lseek - default */ - nfs_file_read, /* read */ - nfs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select - default */ - NULL, /* ioctl - default */ - nfs_file_mmap, /* mmap */ - nfs_open, /* open */ - nfs_file_flush, /* flush */ - nfs_release, /* release */ - nfs_fsync, /* fsync */ - NULL, /* fasync */ - nfs_lock, /* lock */ + read: nfs_file_read, + write: nfs_file_write, + mmap: nfs_file_mmap, + open: nfs_open, + flush: nfs_file_flush, + release: nfs_release, + fsync: nfs_fsync, + lock: nfs_lock, }; struct inode_operations nfs_file_inode_operations = { @@ -69,9 +64,6 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - nfs_readpage, /* readpage */ - nfs_writepage, /* writepage */ NULL, /* truncate */ NULL, /* permission */ nfs_revalidate, /* revalidate */ @@ -146,11 +138,13 @@ nfs_fsync(struct file *file, struct dentry *dentry) dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino); + lock_kernel(); status = nfs_wb_file(inode, file); if (!status) { status = file->f_error; file->f_error = 0; } + unlock_kernel(); return status; } @@ -163,21 +157,29 @@ nfs_fsync(struct file *file, struct dentry *dentry) * If the writer ends up delaying the write, the writer needs to * increment the page use counts until he is done with the page. */ -static int nfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +static int nfs_prepare_write(struct page *page, unsigned offset, unsigned to) +{ + kmap(page); + return 0; +} +static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { long status; - bytes -= copy_from_user((u8*)kmap(page) + offset, buf, bytes); kunmap(page); - status = -EFAULT; - if (bytes) { - lock_kernel(); - status = nfs_updatepage(file, page, offset, bytes); - unlock_kernel(); - } + lock_kernel(); + status = nfs_updatepage(file, page, offset, to-offset); + unlock_kernel(); return status; } +struct address_space_operations nfs_file_aops = { + readpage: nfs_readpage, + writepage: nfs_writepage, + prepare_write: nfs_prepare_write, + commit_write: nfs_commit_write +}; + /* * Write to a file (through the page cache). */ @@ -203,7 +205,7 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) if (!count) goto out; - result = generic_file_write(file, buf, count, ppos, nfs_write_one_page); + result = generic_file_write(file, buf, count, ppos); out: return result; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index aae8d3d0a..c8d42adfe 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -473,9 +473,10 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr) NFS_FILEID(inode) = fattr->fileid; NFS_FSID(inode) = fattr->fsid; inode->i_mode = fattr->mode; - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &nfs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_data.a_ops = &nfs_file_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &nfs_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 09a16b302..a13e26fd0 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -479,7 +479,7 @@ updated: if (req->wb_bytes == PAGE_SIZE) SetPageUptodate(page); - retval = count; + retval = 0; if (synchronous) { int status = wait_on_write_request(req); if (status) { diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 68e7a1dac..b0aac3280 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -29,13 +29,7 @@ #include <linux/nfsd/xdr.h> #include <linux/nfsd/syscall.h> -#if LINUX_VERSION_CODE >= 0x020100 #include <asm/uaccess.h> -#else -# define copy_from_user memcpy_fromfs -# define copy_to_user memcpy_tofs -# define access_ok !verify_area -#endif #include <linux/smp.h> #include <linux/smp_lock.h> @@ -249,10 +243,8 @@ done: #ifdef MODULE /* New-style module support since 2.1.18 */ -#if LINUX_VERSION_CODE >= 131346 EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); -#endif extern int (*do_nfsservctl)(int, void *, void *); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 95bec2fc5..b1af9399e 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -39,9 +39,7 @@ #include <linux/nfsd/nfsfh.h> #include <linux/quotaops.h> -#if LINUX_VERSION_CODE >= 0x020100 #include <asm/uaccess.h> -#endif #define NFSDDBG_FACILITY NFSDDBG_FILEOP #define NFSD_PARANOIA diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index 7de7eb5c4..fff05ca9b 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -405,44 +405,14 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d) } static struct file_operations ntfs_file_operations_nommap = { - NULL, /* lseek */ - ntfs_read, + read: ntfs_read, #ifdef CONFIG_NTFS_RW - ntfs_write, -#else - NULL, + write: ntfs_write, #endif - NULL, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* lock */ }; static struct inode_operations ntfs_inode_operations_nobmap = { &ntfs_file_operations_nommap, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ }; #ifdef CONFIG_NTFS_RW @@ -583,52 +553,28 @@ ntfs_bmap(struct inode *ino,int block) return (ret==-1) ? 0:ret; } +/* It's fscking broken. */ + +static int ntfs_get_block(struct inode *inode, long block, struct buffer_head *bh, int create) +{ + BUG(); + return -1; +} + static struct file_operations ntfs_file_operations = { - NULL, /* lseek */ - ntfs_read, + read: ntfs_read, + mmap: generic_file_mmap, #ifdef CONFIG_NTFS_RW - ntfs_write, -#else - NULL, + write: ntfs_write, #endif - NULL, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - generic_file_mmap, - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* lock */ }; static struct inode_operations ntfs_inode_operations = { &ntfs_file_operations, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - ntfs_bmap, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ }; static struct file_operations ntfs_dir_operations = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - ntfs_readdir, /* readdir */ + readdir: ntfs_readdir, }; static struct inode_operations ntfs_dir_inode_operations = { @@ -650,16 +596,32 @@ static struct inode_operations ntfs_dir_inode_operations = { NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* revalidate */ }; +static int ntfs_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,ntfs_get_block); +} +static int ntfs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,ntfs_get_block); +} +static int ntfs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,ntfs_get_block, + &((struct inode*)page->mapping->host)->u.ntfs_i.mmu_private); +} +static int _ntfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,ntfs_get_block); +} +struct address_space_operations ntfs_aops = { + readpage: ntfs_readpage, + writepage: ntfs_writepage, + prepare_write: ntfs_prepare_write, + commit_write: generic_commit_write, + bmap: _ntfs_bmap +}; /* ntfs_read_inode is called by the Virtual File System (the kernel layer that * deals with filesystems) when iget is called requesting an inode not already * present in the inode table. Typically filesystems have separate @@ -735,8 +697,13 @@ static void ntfs_read_inode(struct inode* inode) } else { - inode->i_op=can_mmap ? &ntfs_inode_operations : - &ntfs_inode_operations_nobmap; + if (can_mmap) { + inode->i_op = &ntfs_inode_operations; + inode->i_mapping->a_ops = &ntfs_aops; + inode->u.ntfs_i.mmu_private = inode->i_size; + } else { + inode->i_op=&ntfs_inode_operations_nobmap; + } inode->i_mode=S_IFREG|S_IRUGO; } #ifdef CONFIG_NTFS_RW @@ -898,16 +898,9 @@ asmlinkage long sys_close(unsigned int fd) */ asmlinkage long sys_vhangup(void) { - int ret = -EPERM; - - if (!capable(CAP_SYS_TTY_CONFIG)) - goto out; - /* If there is a controlling tty, hang it up */ - lock_kernel(); - if (current->tty) + if (capable(CAP_SYS_TTY_CONFIG)) { tty_vhangup(current->tty); - unlock_kernel(); - ret = 0; -out: - return ret; + return 0; + } + return -EPERM; } diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index b09462b89..710c8a7dd 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -1,4 +1,4 @@ -/* $Id: inode.c,v 1.3 2000/01/04 10:02:29 jj Exp $ +/* $Id: inode.c,v 1.4 2000/02/09 22:35:50 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) @@ -553,17 +553,9 @@ int property_release (struct inode *inode, struct file *filp) } static struct file_operations openpromfs_prop_ops = { - NULL, /* lseek - default */ - property_read, /* read */ - property_write, /* write - bad */ - NULL, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - property_release, /* no special release code */ - NULL /* can't fsync */ + read: property_read, + write: property_write, + release: property_release, }; static struct inode_operations openpromfs_prop_inode_ops = { @@ -571,17 +563,7 @@ static struct inode_operations openpromfs_prop_inode_ops = { }; static struct file_operations openpromfs_nodenum_ops = { - NULL, /* lseek - default */ - nodenum_read, /* read */ - NULL, /* write - bad */ - NULL, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ + read: nodenum_read, }; static struct inode_operations openpromfs_nodenum_inode_ops = { @@ -589,17 +571,7 @@ static struct inode_operations openpromfs_nodenum_inode_ops = { }; static struct file_operations openprom_alias_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - openpromfs_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ + readdir: openpromfs_readdir, }; static struct inode_operations openprom_alias_inode_operations = { @@ -608,19 +580,6 @@ static struct inode_operations openprom_alias_inode_operations = { openpromfs_lookup, /* lookup */ NULL, /* link */ openpromfs_unlink, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; extern struct inode_operations openprom_inode_operations; @@ -1019,10 +978,7 @@ static u16 get_nodes (u16 parent, u32 node) static struct file_operations openprom_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - openpromfs_readdir, /* readdir */ + readdir: openpromfs_readdir, }; static struct inode_operations openprom_inode_operations = { diff --git a/fs/partitions/check.c b/fs/partitions/check.c index d9c4c11de..4f6cfd07d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -18,6 +18,7 @@ #include <linux/major.h> #include <linux/blk.h> #include <linux/init.h> +#include <linux/raid/md.h> #include "check.h" @@ -317,6 +318,9 @@ int __init partition_setup(void) #endif rd_load(); #endif +#ifdef CONFIG_BLK_DEV_MD + autodetect_raid(); +#endif #ifdef CONFIG_MD_BOOT md_setup_drive(); #endif diff --git a/fs/partitions/mac.c b/fs/partitions/mac.c index 41364385d..119de17ba 100644 --- a/fs/partitions/mac.c +++ b/fs/partitions/mac.c @@ -28,6 +28,14 @@ extern void note_bootable_part(kdev_t dev, int part); * Code to understand MacOS partition tables. */ +static inline void mac_fix_string(char *stg, int len) +{ + int i; + + for (i = len - 1; i >= 0 && stg[i] == ' '; i--) + stg[i] = 0; +} + int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_part_minor) { struct buffer_head *bh; @@ -35,7 +43,8 @@ int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_ int dev_bsize, dev_pos, pos; unsigned secsize; #ifdef CONFIG_PPC - int first_bootable = 1; + int found_root = 0; + int found_root_goodness = 0; #endif struct mac_partition *part; struct mac_driver_desc *md; @@ -93,16 +102,49 @@ int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_ * If this is the first bootable partition, tell the * setup code, in case it wants to make this the root. */ - if ( (_machine == _MACH_Pmac) && first_bootable - && (be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE) - && strcasecmp(part->processor, "powerpc") == 0) { - note_bootable_part(dev, blk); - first_bootable = 0; + if (_machine == _MACH_Pmac) { + int goodness = 0; + + mac_fix_string(part->processor, 16); + mac_fix_string(part->name, 32); + mac_fix_string(part->type, 32); + + if ((be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE) + && strcasecmp(part->processor, "powerpc") == 0) + goodness++; + + if (strcasecmp(part->type, "Apple_UNIX_SVR2") == 0 + || strcasecmp(part->type, "Linux_PPC") == 0) { + int i, l; + + goodness++; + l = strlen(part->name); + if (strcmp(part->name, "/") == 0) + goodness++; + for (i = 0; i <= l - 4; ++i) { + if (strnicmp(part->name + i, "root", + 4) == 0) { + goodness += 2; + break; + } + } + if (strnicmp(part->name, "swap", 4) == 0) + goodness--; + } + + if (goodness > found_root_goodness) { + found_root = blk; + found_root_goodness = goodness; + } } #endif /* CONFIG_PPC */ ++first_part_minor; } +#ifdef CONFIG_PPC + if (found_root_goodness) + note_bootable_part(dev, found_root); +#endif brelse(bh); printk("\n"); return 1; @@ -462,101 +462,73 @@ pipe_rdwr_open(struct inode *inode, struct file *filp) * are also used in linux/fs/fifo.c to do operations on FIFOs. */ struct file_operations connecting_fifo_fops = { - pipe_lseek, - connect_read, - bad_pipe_w, - NULL, /* no readdir */ - connect_poll, - pipe_ioctl, - NULL, /* no mmap on pipes.. surprise */ - pipe_read_open, - NULL, /* flush */ - pipe_read_release, - NULL + llseek: pipe_lseek, + read: connect_read, + write: bad_pipe_w, + poll: connect_poll, + ioctl: pipe_ioctl, + open: pipe_read_open, + release: pipe_read_release, }; struct file_operations read_fifo_fops = { - pipe_lseek, - pipe_read, - bad_pipe_w, - NULL, /* no readdir */ - fifo_poll, - pipe_ioctl, - NULL, /* no mmap on pipes.. surprise */ - pipe_read_open, - NULL, /* flush */ - pipe_read_release, - NULL + llseek: pipe_lseek, + read: pipe_read, + write: bad_pipe_w, + poll: fifo_poll, + ioctl: pipe_ioctl, + open: pipe_read_open, + release: pipe_read_release, }; struct file_operations write_fifo_fops = { - pipe_lseek, - bad_pipe_r, - pipe_write, - NULL, /* no readdir */ - fifo_poll, - pipe_ioctl, - NULL, /* mmap */ - pipe_write_open, - NULL, /* flush */ - pipe_write_release, - NULL + llseek: pipe_lseek, + read: bad_pipe_r, + write: pipe_write, + poll: fifo_poll, + ioctl: pipe_ioctl, + open: pipe_write_open, + release: pipe_write_release, }; struct file_operations rdwr_fifo_fops = { - pipe_lseek, - pipe_read, - pipe_write, - NULL, /* no readdir */ - fifo_poll, - pipe_ioctl, - NULL, /* mmap */ - pipe_rdwr_open, - NULL, /* flush */ - pipe_rdwr_release, - NULL + llseek: pipe_lseek, + read: pipe_read, + write: pipe_write, + poll: fifo_poll, + ioctl: pipe_ioctl, + open: pipe_rdwr_open, + release: pipe_rdwr_release, }; struct file_operations read_pipe_fops = { - pipe_lseek, - pipe_read, - bad_pipe_w, - NULL, /* no readdir */ - pipe_poll, - pipe_ioctl, - NULL, /* no mmap on pipes.. surprise */ - pipe_read_open, - NULL, /* flush */ - pipe_read_release, - NULL + llseek: pipe_lseek, + read: pipe_read, + write: bad_pipe_w, + poll: pipe_poll, + ioctl: pipe_ioctl, + open: pipe_read_open, + release: pipe_read_release, }; struct file_operations write_pipe_fops = { - pipe_lseek, - bad_pipe_r, - pipe_write, - NULL, /* no readdir */ - pipe_poll, - pipe_ioctl, - NULL, /* mmap */ - pipe_write_open, - NULL, /* flush */ - pipe_write_release, - NULL + llseek: pipe_lseek, + read: bad_pipe_r, + write: pipe_write, + poll: pipe_poll, + ioctl: pipe_ioctl, + open: pipe_write_open, + release: pipe_write_release, }; struct file_operations rdwr_pipe_fops = { - pipe_lseek, - pipe_read, - pipe_write, - NULL, /* no readdir */ - pipe_poll, - pipe_ioctl, - NULL, /* mmap */ - pipe_rdwr_open, - NULL, /* flush */ - pipe_rdwr_release, - NULL + llseek: pipe_lseek, + read: pipe_read, + write: pipe_write, + poll: pipe_poll, + ioctl: pipe_ioctl, + open: pipe_rdwr_open, + release: pipe_rdwr_release, }; static struct inode * get_pipe_inode(void) diff --git a/fs/proc/Makefile b/fs/proc/Makefile index fade4acf2..ad890255e 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -10,9 +10,6 @@ O_TARGET := proc.o O_OBJS := inode.o root.o base.o generic.o array.o \ kmsg.o proc_tty.o proc_misc.o kcore.o -ifdef CONFIG_OMIRR -O_OBJS := $(O_OBJS) omirr.o -endif OX_OBJS := procfs_syms.o M_OBJS := diff --git a/fs/proc/array.c b/fs/proc/array.c index 4ae097ca2..65bc80c88 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -133,8 +133,7 @@ static inline const char * get_task_state(struct task_struct *tsk) TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE | TASK_ZOMBIE | - TASK_STOPPED | - TASK_SWAPPING); + TASK_STOPPED); const char **p = &task_state_array[0]; while (state) { diff --git a/fs/proc/base.c b/fs/proc/base.c index 4670b456c..cdd2116b2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -15,7 +15,6 @@ #include <asm/uaccess.h> -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/proc_fs.h> @@ -206,8 +205,7 @@ static ssize_t pid_maps_read(struct file * file, char * buf, } static struct file_operations proc_maps_operations = { - NULL, /* array_lseek */ - pid_maps_read, + read: pid_maps_read, }; struct inode_operations proc_maps_inode_operations = { @@ -258,8 +256,7 @@ static ssize_t proc_info_read(struct file * file, char * buf, } static struct file_operations proc_info_file_operations = { - NULL, /* lseek */ - proc_info_read, /* read */ + read: proc_info_read, }; static struct inode_operations proc_info_inode_operations = { @@ -348,9 +345,8 @@ static ssize_t mem_write(struct file * file, const char * buf, } static struct file_operations proc_mem_operations = { - NULL, /* lseek - default */ - mem_read, - mem_write, + read: mem_read, + write: mem_write, }; static struct inode_operations proc_mem_inode_operations = { @@ -366,9 +362,6 @@ static struct inode_operations proc_mem_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ proc_permission, /* permission */ NULL /* revalidate */ @@ -456,18 +449,8 @@ out: } static struct inode_operations proc_pid_link_inode_operations = { - NULL, /* file-operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - proc_pid_readlink, /* readlink */ - proc_pid_follow_link, /* follow_link */ + readlink: proc_pid_readlink, + follow_link: proc_pid_follow_link }; /* reading from directory - bad */ @@ -761,10 +744,8 @@ out: } static struct file_operations proc_fd_operations = { - NULL, /* lseek - default */ - proc_dir_read, /* read - bad */ - NULL, /* write - bad */ - proc_readfd, /* readdir */ + read: proc_dir_read, /* read - bad */ + readdir: proc_readfd, /* readdir */ }; /* @@ -783,9 +764,6 @@ static struct inode_operations proc_fd_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ proc_permission, /* permission */ }; @@ -882,10 +860,8 @@ out: } static struct file_operations proc_base_operations = { - NULL, /* lseek - default */ - proc_dir_read, /* read - bad */ - NULL, /* write - bad */ - proc_base_readdir, /* readdir */ + read: proc_dir_read, /* read - bad */ + readdir: proc_base_readdir, /* readdir */ }; static struct inode_operations proc_base_inode_operations = { diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 3017323b2..ad42f4fbc 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -32,9 +32,9 @@ int proc_match(int len, const char *name,struct proc_dir_entry * de) } static struct file_operations proc_file_operations = { - proc_file_lseek, /* lseek */ - proc_file_read, /* read */ - proc_file_write, /* write */ + llseek: proc_file_lseek, + read: proc_file_read, + write: proc_file_write, }; static struct inode_operations proc_file_inode_operations = { @@ -337,10 +337,7 @@ int proc_readdir(struct file * filp, * the /proc directory. */ static struct file_operations proc_dir_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - proc_readdir, /* readdir */ + readdir: proc_readdir, }; /* diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index f63bcac6e..01ac4fe2a 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -27,14 +27,8 @@ static int open_kcore(struct inode * inode, struct file * filp) static ssize_t read_kcore(struct file *, char *, size_t, loff_t *); static struct file_operations proc_kcore_operations = { - NULL, /* lseek */ - read_kcore, - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - open_kcore + read: read_kcore, + open: open_kcore, }; struct inode_operations proc_kcore_inode_operations = { diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index b9344ba55..a8fe0fc3c 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -46,17 +46,10 @@ static unsigned int kmsg_poll(struct file *file, poll_table * wait) static struct file_operations proc_kmsg_operations = { - NULL, /* kmsg_lseek */ - kmsg_read, - NULL, /* kmsg_write */ - NULL, /* kmsg_readdir */ - kmsg_poll, /* kmsg_poll */ - NULL, /* kmsg_ioctl */ - NULL, /* mmap */ - kmsg_open, - NULL, /* flush */ - kmsg_release, - NULL /* can't fsync */ + read: kmsg_read, + poll: kmsg_poll, + open: kmsg_open, + release: kmsg_release, }; struct inode_operations proc_kmsg_inode_operations = { diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c deleted file mode 100644 index bdbcb0cbf..000000000 --- a/fs/proc/omirr.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * fs/proc/omirr.c - online mirror support - * - * (C) 1997 Thomas Schoebel-Theuer - */ - -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/omirr.h> -#include <asm/uaccess.h> - -static int nr_omirr_open = 0; -static int cleared_flag = 0; - -static char * buffer = NULL; -static int read_pos, write_pos; -static int clip_pos, max_pos; -static DECLARE_WAIT_QUEUE_HEAD(read_wait); -static DECLARE_WAIT_QUEUE_HEAD(write_wait); - -static /*inline*/ int reserve_write_space(int len) -{ - int rest = max_pos - write_pos; - - if(rest < len) { - clip_pos = write_pos; - write_pos = 0; - rest = max_pos; - } - while(read_pos > write_pos && read_pos <= write_pos+len) { - if(!nr_omirr_open) - return 0; - interruptible_sleep_on(&write_wait); - } - return 1; -} - -static /*inline*/ void write_space(int len) -{ - write_pos += len; - wake_up_interruptible(&read_wait); -} - -static /*inline*/ int reserve_read_space(int len) -{ - int rest = clip_pos - read_pos; - - if(!rest) { - read_pos = 0; - rest = clip_pos; - clip_pos = max_pos; - } - if(len > rest) - len = rest; - while(read_pos == write_pos) { - interruptible_sleep_on(&read_wait); - } - rest = write_pos - read_pos; - if(rest > 0 && rest < len) - len = rest; - return len; -} - -static /*inline*/ void read_space(int len) -{ - read_pos += len; - if(read_pos >= clip_pos) { - read_pos = 0; - clip_pos = max_pos; - } - wake_up_interruptible(&write_wait); -} - -static /*inline*/ void init_buffer(char * initxt) -{ - int len = initxt ? strlen(initxt) : 0; - - if(!buffer) { - buffer = (char*)__get_free_page(GFP_USER); - max_pos = clip_pos = PAGE_SIZE; - } - read_pos = write_pos = 0; - memcpy(buffer, initxt, len); - write_space(len); -} - -static int omirr_open(struct inode * inode, struct file * file) -{ - if(nr_omirr_open) - return -EAGAIN; - nr_omirr_open++; - if(!buffer) - init_buffer(NULL); - return 0; -} - -static int omirr_release(struct inode * inode, struct file * file) -{ - nr_omirr_open--; - read_space(0); - return 0; -} - -static long omirr_read(struct inode * inode, struct file * file, - char * buf, unsigned long count) -{ - char * tmp; - int len; - int error = 0; - - if(!count) - goto done; - error = -EINVAL; - if(!buf || count < 0) - goto done; - - error = verify_area(VERIFY_WRITE, buf, count); - if(error) - goto done; - - error = -EAGAIN; - if((file->f_flags & O_NONBLOCK) && read_pos == write_pos) - goto done; - - error = len = reserve_read_space(count); - tmp = buffer + read_pos; - while(len) { - put_user(*tmp++, buf++); - len--; - } - read_space(error); -done: - return error; -} - -int compute_name(struct dentry * entry, char * buf) -{ - int len; - - if(IS_ROOT(entry)) { - *buf = '/'; - return 1; - } - len = compute_name(entry->d_parent, buf); - if(len > 1) { - buf[len++] = '/'; - } - memcpy(buf+len, entry->d_name, entry->d_len); - return len + entry->d_len; -} - -int _omirr_print(struct dentry * ent1, struct dentry * ent2, - struct qstr * suffix, const char * fmt, - va_list args1, va_list args2) -{ - int count = strlen(fmt) + 10; /* estimate */ - const char * tmp = fmt; - char lenbuf[8]; - int res; - - if(!buffer) - init_buffer(NULL); - while(*tmp) { - while(*tmp && *tmp++ != '%') ; - if(*tmp) { - if(*tmp == 's') { - char * str = va_arg(args1, char*); - count += strlen(str); - } else { - (void)va_arg(args1, int); - count += 8; /* estimate */ - } - } - } - if(ent1) { - struct dentry * dent = ent1; - while(dent && !IS_ROOT(dent)) { - count += dent->d_len + 1; - dent = dent->d_parent; - } - count++; - if(ent2) { - dent = ent2; - while(dent && !IS_ROOT(dent)) { - count += dent->d_len + 1; - dent = dent->d_parent; - } - count++; - } - if(suffix) - count += suffix->len + 1; - } - - if((nr_omirr_open | cleared_flag) && reserve_write_space(count)) { - cleared_flag = 0; - res = vsprintf(buffer+write_pos+4, fmt, args2) + 4; - if(res > count) - printk("omirr: format estimate was wrong\n"); - if(ent1) { - res += compute_name(ent1, buffer+write_pos+res); - if(ent2) { - buffer[write_pos+res++] = '\0'; - res += compute_name(ent2, buffer+write_pos+res); - } - if(suffix) { - buffer[write_pos+res++] = '/'; - memcpy(buffer+write_pos+res, - suffix->name, suffix->len); - res += suffix->len; - } - buffer[write_pos+res++] = '\0'; - buffer[write_pos+res++] = '\n'; - } - sprintf(lenbuf, "%04d", res); - memcpy(buffer+write_pos, lenbuf, 4); - } else { - if(!cleared_flag) { - cleared_flag = 1; - init_buffer("0007 Z\n"); - } - res = 0; - } - write_space(res); - return res; -} - -int omirr_print(struct dentry * ent1, struct dentry * ent2, - struct qstr * suffix, const char * fmt, ...) -{ - va_list args1, args2; - int res; - - /* I don't know whether I could make a simple copy of the va_list, - * so for the safe way... - */ - va_start(args1, fmt); - va_start(args2, fmt); - res = _omirr_print(ent1, ent2, suffix, fmt, args1, args2); - va_end(args2); - va_end(args1); - return res; -} - -int omirr_printall(struct inode * inode, const char * fmt, ...) -{ - int res = 0; - struct dentry * tmp = inode->i_dentry; - - if(tmp) do { - va_list args1, args2; - va_start(args1, fmt); - va_start(args2, fmt); - res += _omirr_print(tmp, NULL, NULL, fmt, args1, args2); - va_end(args2); - va_end(args1); - tmp = tmp->d_next; - } while(tmp != inode->i_dentry); - return res; -} - -static struct file_operations omirr_operations = { - NULL, /* omirr_lseek */ - omirr_read, - NULL, /* omirr_write */ - NULL, /* omirr_readdir */ - NULL, /* omirr_select */ - NULL, /* omirr_ioctl */ - NULL, /* mmap */ - omirr_open, - NULL, /* flush */ - omirr_release, - NULL, /* fsync */ - NULL, /* fasync */ -}; - -struct inode_operations proc_omirr_inode_operations = { - &omirr_operations, -}; diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 00b09e6a7..3ad4bfabf 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -29,6 +29,7 @@ #include <linux/smp.h> #include <linux/signal.h> #include <linux/module.h> +#include <linux/init.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -593,9 +594,8 @@ static ssize_t write_profile(struct file * file, const char * buf, } static struct file_operations proc_profile_operations = { - NULL, /* lseek */ - read_profile, - write_profile, + read: read_profile, + write: write_profile, }; static struct inode_operations proc_profile_inode_operations = { @@ -618,7 +618,7 @@ static struct proc_dir_entry proc_root_profile = { 0, &proc_profile_inode_operations }; -void proc_misc_init(void) +void __init proc_misc_init(void) { static struct { char *name; diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c index 45d80dd5f..cc1ae7d96 100644 --- a/fs/proc/procfs_syms.c +++ b/fs/proc/procfs_syms.c @@ -2,6 +2,7 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/proc_fs.h> +#include <linux/init.h> extern struct proc_dir_entry *proc_sys_root; @@ -26,7 +27,7 @@ static struct file_system_type proc_fs_type = { NULL }; -int init_proc_fs(void) +int __init init_proc_fs(void) { return register_filesystem(&proc_fs_type) == 0; } diff --git a/fs/proc/root.c b/fs/proc/root.c index 75780167a..8bacabed2 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -143,10 +143,7 @@ static int proc_root_readdir(struct file * filp, * directory handling functions for that.. */ static struct file_operations proc_root_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - proc_root_readdir, /* readdir */ + readdir: proc_root_readdir, }; /* diff --git a/fs/qnx4/Makefile b/fs/qnx4/Makefile index 1988bb8ff..943eb1223 100644 --- a/fs/qnx4/Makefile +++ b/fs/qnx4/Makefile @@ -8,8 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := qnx4.o -O_OBJS := inode.o dir.o namei.o file.o bitmap.o symlinks.o truncate.o \ -fsync.o +O_OBJS := inode.o dir.o namei.o file.o bitmap.o truncate.o fsync.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index 80ab1f590..6eb3ca0a5 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -17,130 +17,6 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/qnx4_fs.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/malloc.h> -#include <linux/fcntl.h> -#include <linux/stat.h> -#include <linux/locks.h> -#include <linux/mm.h> -#include <linux/pagemap.h> - -#include <asm/segment.h> -#include <asm/system.h> -#include <asm/uaccess.h> - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - - -#ifdef CONFIG_QNX4FS_RW -static ssize_t qnx4_file_write(struct file *filp, const char *buf, - size_t count, loff_t * ppos) -{ - struct dentry *dentry = filp->f_dentry; - struct inode *inode = dentry->d_inode; - struct qnx4_inode_info *qnx4_ino; - struct buffer_head *bh; - ssize_t result = -EBUSY, c; - off_t pos; - unsigned long start, block, extent_end; - char *p; - - QNX4DEBUG(("qnx4: file_write(%s/%s (%d), %lu@%lu)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_count, (unsigned long) count, (unsigned long) *ppos)); - if (inode == NULL) { - printk("qnx4: NULL inode for file_write\n"); - return -EINVAL; - } - qnx4_ino = &inode->u.qnx4_i; - if (S_ISREG(inode->i_mode) == 0) { - printk("qnx4: write to non-file, mode %07o\n", inode->i_mode); - return -EINVAL; - } - if (count == 0) { - goto out; - } - if (filp->f_flags & O_APPEND) { - pos = inode->i_size; - } else { - pos = *ppos; - } - start = qnx4_ino->i_first_xtnt.xtnt_blk + ((pos >> 9) * 0) - 1; - result = 0; - extent_end = start + qnx4_ino->i_first_xtnt.xtnt_size - 1; - QNX4DEBUG(("qnx4: extent length : [%lu] bytes\n", - qnx4_ino->i_first_xtnt.xtnt_size)); - while (result < count) { - block = start + pos / QNX4_BLOCK_SIZE; - if (block > extent_end) { - if (qnx4_is_free(inode->i_sb, block) <= 0) { - printk("qnx4: next inode is busy -> write aborted.\n"); - result = -ENOSPC; - break; - } - } - if ((bh = bread(inode->i_dev, block, - QNX4_BLOCK_SIZE)) == NULL) { - printk("qnx4: I/O error on write.\n"); - result = -EIO; - goto out; - } - if (bh == NULL) { - if (result != 0) { - result = -ENOSPC; - } - break; - } - if (block > extent_end) { - qnx4_set_bitmap(inode->i_sb, block, 1); - extent_end++; - qnx4_ino->i_first_xtnt.xtnt_size = extent_end - start + 1; - } - c = QNX4_BLOCK_SIZE - (pos % QNX4_BLOCK_SIZE); - if (c > count - result) { - c = count - result; - } - if (c != QNX4_BLOCK_SIZE && buffer_uptodate(bh) == 0) { - ll_rw_block(WRITE, 1, &bh); - wait_on_buffer(bh); - if (buffer_uptodate(bh) == 0) { - brelse(bh); - if (result != 0) { - result = -EIO; - } - break; - } - } - p = bh->b_data + (pos % QNX4_BLOCK_SIZE); - c -= copy_from_user(p, buf, c); - if (c == 0) { - brelse(bh); - if (result == 0) { - result = -EFAULT; - } - break; - } -// update_vm_cache(inode, pos, p, c); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - brelse(bh); - pos += c; - buf += c; - result += c; - } - if (pos > inode->i_size) { - inode->i_size = pos; - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - - out: - return result; -} -#endif /* * We have mostly NULL's here: the current defaults are ok for @@ -150,7 +26,7 @@ static struct file_operations qnx4_file_operations = { read: generic_file_read, #ifdef CONFIG_QNX4FS_RW - write: qnx4_file_write, + write: generic_file_write, #endif mmap: generic_file_mmap, #ifdef CONFIG_QNX4FS_RW @@ -161,10 +37,7 @@ static struct file_operations qnx4_file_operations = struct inode_operations qnx4_file_inode_operations = { default_file_ops: &qnx4_file_operations, - get_block: qnx4_get_block, - readpage: block_read_full_page, #ifdef CONFIG_QNX4FS_RW - writepage: block_write_full_page, truncate: qnx4_truncate, #endif }; diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c index 4cea74fe5..e90291f03 100644 --- a/fs/qnx4/fsync.c +++ b/fs/qnx4/fsync.c @@ -16,6 +16,7 @@ #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/locks.h> +#include <linux/smp_lock.h> #include <linux/fs.h> #include <linux/qnx4_fs.h> @@ -156,10 +157,12 @@ int qnx4_sync_file(struct file *file, struct dentry *dentry) S_ISLNK(inode->i_mode))) return -EINVAL; + lock_kernel(); for (wait = 0; wait <= 1; wait++) { err |= sync_direct(inode, wait); } err |= qnx4_sync_inode(inode); + unlock_kernel(); return (err < 0) ? -EIO : 0; } diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 5b70d5211..abb6d8260 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -423,6 +423,31 @@ static void qnx4_put_super(struct super_block *sb) return; } +static int qnx4_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,qnx4_get_block); +} +static int qnx4_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,qnx4_get_block); +} +static int qnx4_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,qnx4_get_block, + &((struct inode*)page->mapping->host)->u.qnx4_i.mmu_private); +} +static int qnx4_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,qnx4_get_block); +} +struct address_space_operations qnx4_aops = { + readpage: qnx4_readpage, + writepage: qnx4_writepage, + prepare_write: qnx4_prepare_write, + commit_write: generic_commit_write, + bmap: qnx4_bmap +}; + static void qnx4_read_inode(struct inode *inode) { struct buffer_head *bh; @@ -461,14 +486,17 @@ static void qnx4_read_inode(struct inode *inode) inode->i_blksize = QNX4_DIR_ENTRY_SIZE; memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE); - inode->i_op = &qnx4_file_inode_operations; - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &qnx4_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_mapping->a_ops = &qnx4_aops; + inode->u.qnx4_i.mmu_private = inode->i_size; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &qnx4_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &qnx4_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &qnx4_aops; + inode->u.qnx4_i.mmu_private = inode->i_size; + } else /* HUH??? Where is device number? Oh, well... */ init_special_inode(inode, inode->i_mode, 0); diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c deleted file mode 100644 index 4eaf27034..000000000 --- a/fs/qnx4/symlinks.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * QNX4 file system, Linux implementation. - * - * Version : 0.2.1 - * - * Using parts of the xiafs filesystem. - * - * History : - * - * 28-05-1998 by Richard Frowijn : first release. - * 21-06-1998 by Frank Denis : ugly changes to make it compile on Linux 2.1.99+ - */ - -#include <linux/fs.h> -#include <linux/qnx4_fs.h> - -/* - * symlinks can't do much... - */ -struct inode_operations qnx4_symlink_inode_operations = -{ - readlink: page_readlink, - follow_link: page_follow_link, - get_block: qnx4_get_block, - readpage: block_read_full_page, -}; diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 692754912..ab5972591 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -446,47 +446,21 @@ romfs_readpage(struct dentry * dentry, struct page * page) /* Mapping from our types to the kernel */ +static struct address_space_operations romfs_aops = { + readpage: romfs_readpage +}; + static struct file_operations romfs_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - NULL, /* write - bad */ - NULL, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ + read: generic_file_read, + mmap: generic_file_mmap, }; static struct inode_operations romfs_file_inode_operations = { &romfs_file_operations, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block -- not really */ - romfs_readpage, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static struct file_operations romfs_dir_operations = { - NULL, /* lseek - default */ - NULL, /* read */ - NULL, /* write - bad */ - romfs_readdir, /* readdir */ + readdir: romfs_readdir, }; /* Merged dir/symlink op table. readdir/lookup/readlink/follow_link @@ -497,27 +471,6 @@ static struct inode_operations romfs_dir_inode_operations = { &romfs_dir_operations, NULL, /* create */ romfs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ -}; - -static struct inode_operations romfs_link_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - readpage: romfs_readpage }; static mode_t romfs_modemap[] = @@ -531,7 +484,7 @@ static struct inode_operations *romfs_inoops[] = NULL, /* hardlink, handled elsewhere */ &romfs_dir_inode_operations, &romfs_file_inode_operations, - &romfs_link_inode_operations, + &page_symlink_inode_operations, NULL, /* device/fifo/socket nodes, */ NULL, /* set by init_special_inode */ NULL, @@ -587,6 +540,8 @@ romfs_read_inode(struct inode *i) i->i_mode = ino; if (S_ISDIR(ino)) i->i_size = i->u.romfs_i.i_metasize; + else + i->i_data.a_ops = &romfs_aops; } else { /* depending on MBZ for sock/fifos */ nextfh = ntohl(ri.spec); diff --git a/fs/select.c b/fs/select.c index 7df7e0c9b..33e54a9fa 100644 --- a/fs/select.c +++ b/fs/select.c @@ -43,6 +43,39 @@ * Linus noticed. -- jrs */ +static poll_table* alloc_wait(int nfds) +{ + poll_table* out; + poll_table* walk; + + out = (poll_table *) __get_free_page(GFP_KERNEL); + if(out==NULL) + return NULL; + out->nr = 0; + out->entry = (struct poll_table_entry *)(out + 1); + out->next = NULL; + nfds -=__MAX_POLL_TABLE_ENTRIES; + walk = out; + while(nfds > 0) { + poll_table *tmp = (poll_table *) __get_free_page(GFP_KERNEL); + if (!tmp) { + while(out != NULL) { + tmp = out->next; + free_page((unsigned long)out); + out = tmp; + } + return NULL; + } + tmp->nr = 0; + tmp->entry = (struct poll_table_entry *)(tmp + 1); + tmp->next = NULL; + walk->next = tmp; + walk = tmp; + nfds -=__MAX_POLL_TABLE_ENTRIES; + } + return out; +} + static void free_wait(poll_table * p) { struct poll_table_entry * entry; @@ -67,7 +100,6 @@ void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table for (;;) { if (p->nr < __MAX_POLL_TABLE_ENTRIES) { struct poll_table_entry * entry; -ok_table: entry = p->entry + p->nr; get_file(filp); entry->filp = filp; @@ -77,17 +109,6 @@ ok_table: p->nr++; return; } - if (p->next == NULL) { - poll_table *tmp = (poll_table *) __get_free_page(GFP_KERNEL); - if (!tmp) - return; - tmp->nr = 0; - tmp->entry = (struct poll_table_entry *)(tmp + 1); - tmp->next = NULL; - p->next = tmp; - p = tmp; - goto ok_table; - } p = p->next; } } @@ -152,33 +173,28 @@ get_max: int do_select(int n, fd_set_bits *fds, long *timeout) { - poll_table *wait_table, *wait; + poll_table *wait, *orig_wait; int retval, i, off; long __timeout = *timeout; - wait = wait_table = NULL; - if (__timeout) { - wait_table = (poll_table *) __get_free_page(GFP_KERNEL); - if (!wait_table) - return -ENOMEM; - - wait_table->nr = 0; - wait_table->entry = (struct poll_table_entry *)(wait_table + 1); - wait_table->next = NULL; - wait = wait_table; - } + orig_wait = wait = NULL; - read_lock(¤t->files->file_lock); + read_lock(¤t->files->file_lock); retval = max_select_fd(n, fds); read_unlock(¤t->files->file_lock); - lock_kernel(); if (retval < 0) - goto out; + return retval; n = retval; + if (__timeout) { + orig_wait = wait = alloc_wait(n); + if (!wait) + return -ENOMEM; + } retval = 0; for (;;) { set_current_state(TASK_INTERRUPTIBLE); + lock_kernel(); for (i = 0 ; i < n; i++) { unsigned long bit = BIT(i); unsigned long mask; @@ -211,6 +227,7 @@ int do_select(int n, fd_set_bits *fds, long *timeout) wait = NULL; } } + unlock_kernel(); wait = NULL; if (retval || !__timeout || signal_pending(current)) break; @@ -218,15 +235,12 @@ int do_select(int n, fd_set_bits *fds, long *timeout) } current->state = TASK_RUNNING; -out: - if (*timeout) - free_wait(wait_table); + free_wait(orig_wait); /* * Up-to-date the caller timeout. */ *timeout = __timeout; - unlock_kernel(); return retval; } @@ -334,29 +348,36 @@ out_nofds: #define POLLFD_PER_PAGE ((PAGE_SIZE) / sizeof(struct pollfd)) -static void do_pollfd(struct pollfd * fdp, poll_table * wait, int *count) +static void do_pollfd(unsigned int num, struct pollfd * fdpage, + poll_table ** pwait, int *count) { - int fd; - unsigned int mask; - - mask = 0; - fd = fdp->fd; - if (fd >= 0) { - struct file * file = fget(fd); - mask = POLLNVAL; - if (file != NULL) { - mask = DEFAULT_POLLMASK; - if (file->f_op && file->f_op->poll) - mask = file->f_op->poll(file, wait); - mask &= fdp->events | POLLERR | POLLHUP; - fput(file); - } - if (mask) { - wait = NULL; - (*count)++; + int i; + + for (i = 0; i < num; i++) { + int fd; + unsigned int mask; + struct pollfd *fdp; + + mask = 0; + fdp = fdpage+i; + fd = fdp->fd; + if (fd >= 0) { + struct file * file = fget(fd); + mask = POLLNVAL; + if (file != NULL) { + mask = DEFAULT_POLLMASK; + if (file->f_op && file->f_op->poll) + mask = file->f_op->poll(file, *pwait); + mask &= fdp->events | POLLERR | POLLHUP; + fput(file); + } + if (mask) { + *pwait = NULL; + (*count)++; + } } + fdp->revents = mask; } - fdp->revents = mask; } static int do_poll(unsigned int nfds, unsigned int nchunks, unsigned int nleft, @@ -365,16 +386,13 @@ static int do_poll(unsigned int nfds, unsigned int nchunks, unsigned int nleft, int count = 0; for (;;) { - unsigned int i, j; + unsigned int i; set_current_state(TASK_INTERRUPTIBLE); for (i=0; i < nchunks; i++) - for (j = 0; j < POLLFD_PER_PAGE; j++) - do_pollfd(fds[i] + j, wait, &count); + do_pollfd(POLLFD_PER_PAGE, fds[i], &wait, &count); if (nleft) - for (j = 0; j < nleft; j++) - do_pollfd(fds[nchunks] + j, wait, &count); - + do_pollfd(nleft, fds[nchunks], &wait, &count); wait = NULL; if (count || !timeout || signal_pending(current)) break; @@ -388,7 +406,7 @@ asmlinkage long sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) { int i, j, fdcount, err; struct pollfd **fds; - poll_table *wait_table = NULL, *wait = NULL; + poll_table *wait = NULL; int nchunks, nleft; /* Do a sanity check on nfds ... */ @@ -403,16 +421,12 @@ asmlinkage long sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) timeout = MAX_SCHEDULE_TIMEOUT; } - err = -ENOMEM; if (timeout) { - wait_table = (poll_table *) __get_free_page(GFP_KERNEL); - if (!wait_table) - goto out; - wait_table->nr = 0; - wait_table->entry = (struct poll_table_entry *)(wait_table + 1); - wait_table->next = NULL; - wait = wait_table; + wait = alloc_wait(nfds); + if (!wait) + return -ENOMEM; } + err = -ENOMEM; fds = NULL; if (nfds != 0) { @@ -473,7 +487,6 @@ out_fds: if (nfds != 0) kfree(fds); out: - if (wait) - free_wait(wait_table); + free_wait(wait); return err; } diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 37bf168f6..68351b4b4 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -32,17 +32,10 @@ static int smb_rename(struct inode *, struct dentry *, static struct file_operations smb_dir_operations = { - NULL, /* lseek - default */ - smb_dir_read, /* read - bad */ - NULL, /* write - bad */ - smb_readdir, /* readdir */ - NULL, /* poll - default */ - smb_ioctl, /* ioctl */ - NULL, /* mmap */ - smb_dir_open, /* open(struct inode *, struct file *) */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* fsync */ + read: smb_dir_read, + readdir: smb_readdir, + ioctl: smb_ioctl, + open: smb_dir_open, }; struct inode_operations smb_dir_inode_operations = @@ -59,9 +52,6 @@ struct inode_operations smb_dir_inode_operations = smb_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ NULL, /* permission */ smb_revalidate_inode, /* revalidate */ diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index f5aff8730..a62de1256 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -260,20 +260,31 @@ out: * If the writer ends up delaying the write, the writer needs to * increment the page use counts until he is done with the page. */ -static int smb_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +static int smb_prepare_write(struct page *page, unsigned offset, unsigned to) +{ + kmap(page); + return 0; +} + +static int smb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { int status; - bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); status = -EFAULT; - if (bytes) { - lock_kernel(); - status = smb_updatepage(file, page, offset, bytes); - unlock_kernel(); - } + lock_kernel(); + status = smb_updatepage(file, page, offset, to-offset); + unlock_kernel(); + kunmap(page); return status; } +struct address_space_operations smb_file_aops = { + readpage: smb_readpage, + writepage: smb_writepage, + prepare_write: smb_prepare_write, + commit_write: smb_commit_write +}; + /* * Write to a file (through the page cache). */ @@ -305,7 +316,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result); if (count > 0) { - result = generic_file_write(file, buf, count, ppos, smb_write_one_page); + result = generic_file_write(file, buf, count, ppos); #ifdef SMBFS_DEBUG_VERBOSE printk("smb_file_write: pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", (long) file->f_pos, dentry->d_inode->i_size, dentry->d_inode->i_mtime, @@ -367,19 +378,13 @@ printk("smb_file_permission: mode=%x, mask=%x\n", mode, mask); static struct file_operations smb_file_operations = { - NULL, /* lseek - default */ - smb_file_read, /* read */ - smb_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - smb_ioctl, /* ioctl */ - smb_file_mmap, /* mmap(struct file*, struct vm_area_struct*) */ - smb_file_open, /* open(struct inode*, struct file*) */ - NULL, /* flush */ - smb_file_release, /* release(struct inode*, struct file*) */ - smb_fsync, /* fsync(struct file*, struct dentry*) */ - NULL, /* fasync(struct file*, int) */ - NULL /* lock(struct file*, int, struct file_lock*) */ + read: smb_file_read, + write: smb_file_write, + ioctl: smb_ioctl, + mmap: smb_file_mmap, + open: smb_file_open, + release: smb_file_release, + fsync: smb_fsync, }; struct inode_operations smb_file_inode_operations = @@ -396,9 +401,6 @@ struct inode_operations smb_file_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - smb_readpage, /* readpage */ - smb_writepage, /* writepage */ NULL, /* truncate */ smb_file_permission, /* permission */ smb_revalidate_inode, /* revalidate */ diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index b4dcc2a32..c071263ff 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -82,9 +82,10 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr) result->i_ino = fattr->f_ino; memset(&(result->u.smbfs_i), 0, sizeof(result->u.smbfs_i)); smb_set_inode_attr(result, fattr); - if (S_ISREG(result->i_mode)) + if (S_ISREG(result->i_mode)) { result->i_op = &smb_file_inode_operations; - else if (S_ISDIR(result->i_mode)) + result->i_data.a_ops = &smb_file_aops; + } else if (S_ISDIR(result->i_mode)) result->i_op = &smb_dir_inode_operations; else result->i_op = NULL; @@ -284,7 +284,7 @@ asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz) /* ---------- LFS-64 ----------- */ -#if !defined(__alpha__) && !defined(__mips64) +#if !defined(__alpha__) && !defined (__ia64__) && !defined(__mips64) static long cp_new_stat64(struct inode * inode, struct stat64 * statbuf) { diff --git a/fs/super.c b/fs/super.c index 71b38fd46..4e7146fcf 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1378,7 +1378,10 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) bdev = do_umount(old_root_dev,1, 0); if (!IS_ERR(bdev)) { printk("okay\n"); - invalidate_buffers(old_root_dev); + /* special: the old device driver is going to be + a ramdisk and the point of this call is to free its + protected memory (even if dirty). */ + destroy_buffers(old_root_dev); if (bdev) { blkdev_put(bdev, BDEV_FS); bdput(bdev); diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile index 6c31360fd..394af0b4d 100644 --- a/fs/sysv/Makefile +++ b/fs/sysv/Makefile @@ -8,8 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := sysv.o -O_OBJS := ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \ - fsync.o truncate.o +O_OBJS := ialloc.o balloc.o inode.o file.o dir.o namei.o fsync.o truncate.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index f17fb8b63..f21a47578 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -30,17 +30,9 @@ static ssize_t sysv_dir_read(struct file * filp, char * buf, static int sysv_readdir(struct file *, void *, filldir_t); static struct file_operations sysv_dir_operations = { - NULL, /* lseek - default */ - sysv_dir_read, /* read */ - NULL, /* write - bad */ - sysv_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - file_fsync /* default fsync */ + read: sysv_dir_read, + readdir: sysv_readdir, + fsync: file_fsync, }; /* @@ -57,14 +49,6 @@ struct inode_operations sysv_dir_inode_operations = { sysv_rmdir, /* rmdir */ sysv_mknod, /* mknod */ sysv_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 2a0d03850..b60a1d02e 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -23,59 +23,18 @@ #include <linux/locks.h> #include <linux/pagemap.h> -#include <asm/uaccess.h> - -#define NBUF 32 - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - -/* - * Write to a file (through the page cache). - */ -static ssize_t -sysv_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - return generic_file_write(file, buf, count, - ppos, block_write_partial_page); -} - /* * We have mostly NULLs here: the current defaults are OK for * the coh filesystem. */ static struct file_operations sysv_file_operations = { - NULL, /* lseek - default */ - generic_file_read, /* read */ - sysv_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - sysv_sync_file, /* fsync */ - NULL, /* fasync */ + read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap, + fsync: sysv_sync_file, }; struct inode_operations sysv_file_inode_operations = { - &sysv_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - sysv_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ - sysv_truncate, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ + &sysv_file_operations, + truncate: sysv_truncate, }; diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index b0e1138c7..3c9871be6 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -16,9 +16,9 @@ #include <linux/errno.h> #include <linux/stat.h> - #include <linux/fs.h> #include <linux/sysv_fs.h> +#include <linux/smp_lock.h> /* return values: 0 means OK/done, 1 means redo, -1 means I/O error. */ @@ -187,6 +187,7 @@ int sysv_sync_file(struct file * file, struct dentry *dentry) S_ISLNK(inode->i_mode))) return -EINVAL; + lock_kernel(); for (wait=0; wait<=1; wait++) { err |= sync_direct(inode, wait); err |= sync_indirect(inode, inode->u.sysv_i.i_data+10, 0, wait); @@ -194,5 +195,6 @@ int sysv_sync_file(struct file * file, struct dentry *dentry) err |= sync_tindirect(inode, inode->u.sysv_i.i_data+12, 0, wait); } err |= sysv_sync_inode (inode); + unlock_kernel(); return (err < 0) ? -EIO : 0; } diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index cfe016728..6580b6125 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -832,7 +832,7 @@ out: return result; } -int sysv_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) +static int sysv_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { struct super_block *sb; int ret, err, new; @@ -961,6 +961,30 @@ struct buffer_head *sysv_file_bread(struct inode *inode, int block, int create) return NULL; } +static int sysv_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,sysv_get_block); +} +static int sysv_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,sysv_get_block); +} +static int sysv_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,sysv_get_block); +} +static int sysv_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,sysv_get_block); +} +struct address_space_operations sysv_aops = { + readpage: sysv_readpage, + writepage: sysv_writepage, + prepare_write: sysv_prepare_write, + commit_write: generic_commit_write, + bmap: sysv_bmap +}; + #ifdef __BIG_ENDIAN static inline unsigned long read3byte (unsigned char * p) @@ -1059,13 +1083,15 @@ static void sysv_read_inode(struct inode *inode) for (block = 0; block < 10+1+1+1; block++) inode->u.sysv_i.i_data[block] = read3byte(&raw_inode->i_a.i_addb[3*block]); - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &sysv_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_mapping->a_ops = &sysv_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &sysv_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &sysv_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &sysv_aops; + } else init_special_inode(inode, inode->i_mode,raw_inode->i_a.i_rdev); brelse(bh); } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 844912898..9661af99c 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -204,6 +204,7 @@ int sysv_create(struct inode * dir, struct dentry * dentry, int mode) if (!inode) return -ENOSPC; inode->i_op = &sysv_file_inode_operations; + inode->i_mapping->a_ops = &sysv_aops; inode->i_mode = mode; mark_inode_dirty(inode); error = sysv_add_entry(dir, dentry->d_name.name, @@ -455,7 +456,8 @@ int sysv_symlink(struct inode * dir, struct dentry * dentry, goto out; inode->i_mode = S_IFLNK | 0777; - inode->i_op = &sysv_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &sysv_aops; err = block_symlink(inode, symname, l); if (err) goto out_no_entry; diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c deleted file mode 100644 index 3f77f831e..000000000 --- a/fs/sysv/symlink.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * linux/fs/sysv/symlink.c - * - * minix/symlink.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * coh/symlink.c - * Copyright (C) 1993 Pascal Haible, Bruno Haible - * - * sysv/symlink.c - * Copyright (C) 1993 Bruno Haible - * - * SystemV/Coherent symlink handling code - */ - -#include <linux/sysv_fs.h> - -/* - * symlinks can't do much... - */ -struct inode_operations sysv_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - get_block: sysv_get_block, - readpage: block_read_full_page -}; diff --git a/fs/udf/dir.c b/fs/udf/dir.c index 916683186..963d20344 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -50,19 +50,9 @@ static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *); /* readdir and lookup functions */ static struct file_operations udf_dir_operations = { - NULL, /* lllseek */ - NULL, /* read */ - NULL, /* write */ - udf_readdir, /* readdir */ - NULL, /* poll */ - udf_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - udf_sync_file, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ + readdir: udf_readdir, + ioctl: udf_ioctl, + fsync: udf_sync_file, }; struct inode_operations udf_dir_inode_operations = { @@ -90,14 +80,6 @@ struct inode_operations udf_dir_inode_operations = { NULL, /* mknod */ NULL, /* rename */ #endif - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; /* diff --git a/fs/udf/file.c b/fs/udf/file.c index 8ea44d2a8..5cf51cd0e 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -72,110 +72,119 @@ static loff_t udf_file_llseek(struct file * file, loff_t offset, int origin) return offset; } -static inline void remove_suid(struct inode * inode) +static int udf_adinicb_readpage(struct dentry *dentry, struct page * page) { - unsigned int mode; + struct inode *inode = dentry->d_inode; - /* set S_IGID if S_IXGRP is set, and always set S_ISUID */ - mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID; - - /* was any of the uid bits set? */ - mode &= inode->i_mode; - if (mode && !capable(CAP_FSETID)) - { - inode->i_mode &= ~mode; - mark_inode_dirty(inode); - } -} - -static ssize_t udf_file_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - ssize_t retval; - struct inode *inode = file->f_dentry->d_inode; + struct buffer_head *bh; + unsigned long kaddr = 0; - retval = generic_file_write(file, buf, count, ppos, block_write_partial_page); + if (!PageLocked(page)) + PAGE_BUG(page); - if (retval > 0) - { - remove_suid(inode); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; - mark_inode_dirty(inode); - } - return retval; + kaddr = kmap(page); + memset((char *)kaddr, 0, PAGE_CACHE_SIZE); + bh = getblk (inode->i_dev, inode->i_ino, inode->i_sb->s_blocksize); + ll_rw_block (READ, 1, &bh); + wait_on_buffer(bh); + memcpy((char *)kaddr, bh->b_data + udf_ext0_offset(inode), + inode->i_size); + brelse(bh); + SetPageUptodate(page); + kunmap(page); + UnlockPage(page); + return 0; } -int udf_write_partial_page_adinicb(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +static int udf_adinicb_writepage(struct dentry *dentry, struct page *page) { - struct inode *inode = file->f_dentry->d_inode; - int err = 0, block; + struct inode *inode = dentry->d_inode; + struct buffer_head *bh; unsigned long kaddr = 0; if (!PageLocked(page)) BUG(); - if (offset < 0 || offset >= (inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode))) - BUG(); - if (bytes+offset < 0 || bytes+offset > (inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode))) - BUG(); kaddr = kmap(page); - block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); - bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); + bh = getblk (inode->i_dev, inode->i_ino, inode->i_sb->s_blocksize); if (!buffer_uptodate(bh)) { ll_rw_block (READ, 1, &bh); wait_on_buffer(bh); } - err = copy_from_user((char *)kaddr + offset, buf, bytes); + memcpy(bh->b_data + udf_ext0_offset(inode), (char *)kaddr, + inode->i_size); + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer(bh); + brelse(bh); + SetPageUptodate(page); + kunmap(page); + return 0; +} + +static int udf_adinicb_prepare_write(struct page *page, unsigned offset, unsigned to) +{ + kmap(page); + return 0; +} + +static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) +{ + struct inode *inode = file->f_dentry->d_inode; + struct buffer_head *bh; + char *kaddr = (char*)page_address(page); + bh = bread (inode->i_dev, inode->i_ino, inode->i_sb->s_blocksize); + if (!buffer_uptodate(bh)) { + ll_rw_block (READ, 1, &bh); + wait_on_buffer(bh); + } memcpy(bh->b_data + udf_file_entry_alloc_offset(inode) + offset, - (char *)kaddr + offset, bytes); + kaddr + offset, to-offset); mark_buffer_dirty(bh, 0); brelse(bh); kunmap(page); SetPageUptodate(page); - return bytes; + return 0; } -static ssize_t udf_file_write_adinicb(struct file * file, const char * buf, +struct address_space_operations udf_adinicb_aops = { + readpage: udf_adinicb_readpage, + writepage: udf_adinicb_writepage, + prepare_write: udf_adinicb_prepare_write, + commit_write: udf_adinicb_commit_write +}; + +static ssize_t udf_file_write(struct file * file, const char * buf, size_t count, loff_t *ppos) { ssize_t retval; struct inode *inode = file->f_dentry->d_inode; int err, pos; - if (file->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = *ppos; - - if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + - pos + count)) - { - udf_expand_file_adinicb(file, pos + count, &err); - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) - { - udf_debug("udf_expand_adinicb: err=%d\n", err); - return err; - } - else - return udf_file_write(file, buf, count, ppos); - } - else - { - if (pos + count > inode->i_size) - UDF_I_LENALLOC(inode) = pos + count; + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) { + if (file->f_flags & O_APPEND) + pos = inode->i_size; else - UDF_I_LENALLOC(inode) = inode->i_size; + pos = *ppos; + + if (inode->i_sb->s_blocksize < + (udf_file_entry_alloc_offset(inode) + pos + count)) { + udf_expand_file_adinicb(file, pos + count, &err); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) { + udf_debug("udf_expand_adinicb: err=%d\n", err); + return err; + } + } else { + if (pos + count > inode->i_size) + UDF_I_LENALLOC(inode) = pos + count; + else + UDF_I_LENALLOC(inode) = inode->i_size; + } } - retval = generic_file_write(file, buf, count, ppos, udf_write_partial_page_adinicb); - - if (retval > 0) - { - remove_suid(inode); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; + retval = generic_file_write(file, buf, count, ppos); + if (retval > 0) { UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; mark_inode_dirty(inode); } @@ -338,83 +347,19 @@ static int udf_open_file(struct inode * inode, struct file * filp) } static struct file_operations udf_file_operations = { - udf_file_llseek, /* llseek */ - generic_file_read, /* read */ - udf_file_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - udf_ioctl, /* ioctl */ - generic_file_mmap, /* mmap */ - udf_open_file, /* open */ - NULL, /* flush */ - udf_release_file, /* release */ - udf_sync_file, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ + llseek: udf_file_llseek, + read: generic_file_read, + write: udf_file_write, + ioctl: udf_ioctl, + mmap: generic_file_mmap, + open: udf_open_file, + release: udf_release_file, + fsync: udf_sync_file, }; struct inode_operations udf_file_inode_operations = { &udf_file_operations, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - udf_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ -#if CONFIG_UDF_RW == 1 - udf_truncate, /* truncate */ -#else - NULL, /* truncate */ -#endif - NULL, /* permission */ - NULL /* revalidate */ -}; - -static struct file_operations udf_file_operations_adinicb = { - udf_file_llseek, /* llseek */ - generic_file_read, /* read */ - udf_file_write_adinicb, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - udf_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - udf_release_file, /* release */ - udf_sync_file_adinicb, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ -}; - -struct inode_operations udf_file_inode_operations_adinicb = { - &udf_file_operations_adinicb, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - udf_get_block, /* get_block */ - udf_readpage_adinicb, /* readpage */ - udf_writepage_adinicb, /* writepage */ #if CONFIG_UDF_RW == 1 - udf_truncate_adinicb, /* truncate */ -#else - NULL, /* truncate */ + truncate: udf_truncate, #endif - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c index b5c10c91b..e7d067e62 100644 --- a/fs/udf/fsync.c +++ b/fs/udf/fsync.c @@ -27,6 +27,7 @@ #include <linux/fs.h> #include <linux/locks.h> +#include <linux/smp_lock.h> #include <linux/udf_fs.h> #include "udf_i.h" @@ -100,6 +101,7 @@ int udf_sync_file(struct file * file, struct dentry *dentry) int wait, err = 0; struct inode *inode = dentry->d_inode; + lock_kernel(); if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) { /* @@ -116,10 +118,6 @@ int udf_sync_file(struct file * file, struct dentry *dentry) } skip: err |= udf_sync_inode (inode); + unlock_kernel(); return err ? -EIO : 0; } - -int udf_sync_file_adinicb(struct file * file, struct dentry *dentry) -{ - return udf_sync_inode(dentry->d_inode) ? -EIO : 0; -} diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 2ea1f980b..752a00339 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -56,6 +56,7 @@ static void udf_merge_extents(struct inode *, static void udf_update_extents(struct inode *, long_ad [EXTENT_MERGE_SIZE], int, int, lb_addr, Uint32, struct buffer_head **); +static int udf_get_block(struct inode *, long, struct buffer_head *, int); /* * udf_put_inode @@ -96,7 +97,7 @@ void udf_delete_inode(struct inode * inode) { inode->i_size = 0; if (inode->i_blocks) - inode->i_op->truncate(inode); + udf_truncate(inode); udf_free_inode(inode); } @@ -117,6 +118,30 @@ static int udf_alloc_block(struct inode *inode, Uint16 partition, return result; } +static int udf_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,udf_get_block); +} +static int udf_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,udf_get_block); +} +static int udf_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,udf_get_block); +} +static int udf_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,udf_get_block); +} +static struct address_space_operations udf_aops = { + readpage: udf_readpage, + writepage: udf_writepage, + prepare_write: udf_prepare_write, + commit_write: generic_commit_write, + bmap: udf_bmap +}; + void udf_expand_file_adinicb(struct file * filp, int newsize, int * err) { struct inode * inode = filp->f_dentry->d_inode; @@ -124,12 +149,13 @@ void udf_expand_file_adinicb(struct file * filp, int newsize, int * err) struct page *page; unsigned long kaddr = 0; + /* from now on we have normal address_space methods */ + inode->i_data.a_ops = &udf_aops; + if (!UDF_I_LENALLOC(inode)) { UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; mark_inode_dirty(inode); - inode->i_op = &udf_file_inode_operations; - filp->f_op = inode->i_op->default_file_ops; return; } @@ -156,14 +182,12 @@ void udf_expand_file_adinicb(struct file * filp, int newsize, int * err) mark_buffer_dirty(bh, 1); udf_release_data(bh); - block_write_full_page(filp->f_dentry, page); + inode->i_data.a_ops->writepage(filp->f_dentry, page); UnlockPage(page); page_cache_release(page); mark_inode_dirty(inode); inode->i_version ++; - inode->i_op = &udf_file_inode_operations; - filp->f_op = inode->i_op->default_file_ops; } struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) @@ -251,34 +275,7 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int return dbh; } -struct buffer_head * udf_getblk(struct inode * inode, long block, - int create, int * err) -{ - struct buffer_head dummy; - int error; - - dummy.b_state = 0; - dummy.b_blocknr = -1000; - error = udf_get_block(inode, block, &dummy, create); - *err = error; - if (!error & buffer_mapped(&dummy)) - { - struct buffer_head *bh; - bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); - if (buffer_new(&dummy)) - { - if (!buffer_uptodate(bh)) - wait_on_buffer(bh); - memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - } - return bh; - } - return NULL; -} - -int udf_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) +static int udf_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) { int err, new; struct buffer_head *bh; @@ -335,6 +332,33 @@ abort_negative: goto abort; } +struct buffer_head * udf_getblk(struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = udf_get_block(inode, block, &dummy, create); + *err = error; + if (!error & buffer_mapped(&dummy)) + { + struct buffer_head *bh; + bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); + if (buffer_new(&dummy)) + { + if (!buffer_uptodate(bh)) + wait_on_buffer(bh); + memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + } + return bh; + } + return NULL; +} + static struct buffer_head * inode_getblk(struct inode * inode, long block, int *err, long *phys, int *new) { @@ -1061,9 +1085,10 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) case FILE_TYPE_NONE: { if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) - inode->i_op = &udf_file_inode_operations_adinicb; + inode->i_data.a_ops = &udf_adinicb_aops; else - inode->i_op = &udf_file_inode_operations; + inode->i_data.a_ops = &udf_aops; + inode->i_op = &udf_file_inode_operations; inode->i_mode |= S_IFREG; break; } @@ -1084,7 +1109,8 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) } case FILE_TYPE_SYMLINK: { - inode->i_op = &udf_symlink_inode_operations; + inode->i_data.a_ops = &udf_symlink_aops; + inode->i_op = &page_symlink_inode_operations; inode->i_mode = S_IFLNK|S_IRWXUGO; break; } @@ -1953,58 +1979,3 @@ long udf_block_map(struct inode *inode, long block) unlock_kernel(); return ret; } - -int udf_readpage_adinicb (struct dentry *dentry, struct page * page) -{ - struct inode *inode = dentry->d_inode; - - struct buffer_head *bh; - int block; - unsigned long kaddr = 0; - - if (!PageLocked(page)) - PAGE_BUG(page); - - kaddr = kmap(page); - memset((char *)kaddr, 0, PAGE_CACHE_SIZE); - block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); - bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); - ll_rw_block (READ, 1, &bh); - wait_on_buffer(bh); - memcpy((char *)kaddr, bh->b_data + udf_ext0_offset(inode), - inode->i_size); - brelse(bh); - SetPageUptodate(page); - kunmap(page); - UnlockPage(page); - return 0; -} - -int udf_writepage_adinicb (struct dentry *dentry, struct page *page) -{ - struct inode *inode = dentry->d_inode; - - struct buffer_head *bh; - int block; - unsigned long kaddr = 0; - - if (!PageLocked(page)) - BUG(); - - kaddr = kmap(page); - block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); - bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); - if (!buffer_uptodate(bh)) - { - ll_rw_block (READ, 1, &bh); - wait_on_buffer(bh); - } - memcpy(bh->b_data + udf_ext0_offset(inode), (char *)kaddr, - inode->i_size); - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer(bh); - brelse(bh); - SetPageUptodate(page); - kunmap(page); - return 0; -} diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 1f54833e4..aff491ef5 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -642,7 +642,8 @@ int udf_create(struct inode *dir, struct dentry *dentry, int mode) if (!inode) return err; - inode->i_op = &udf_file_inode_operations_adinicb; + inode->i_data.a_ops = &udf_adinicb_aops; + inode->i_op = &udf_file_inode_operations; inode->i_mode = mode; mark_inode_dirty(inode); @@ -970,7 +971,8 @@ int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname goto out; inode->i_mode = S_IFLNK | S_IRWXUGO; - inode->i_op = &udf_symlink_inode_operations; + inode->i_data.a_ops = &udf_symlink_aops; + inode->i_op = &page_symlink_inode_operations; bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); ea = bh->b_data + udf_file_entry_alloc_offset(inode); diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 6e5dd233a..5a286ce84 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -121,23 +121,6 @@ out: /* * symlinks can't do much... */ -struct inode_operations udf_symlink_inode_operations = { - NULL, /* no file-operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - page_readlink, /* readlink */ - page_follow_link, /* follow_link */ - NULL, /* get_block */ - udf_symlink_filler, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ +struct address_space_operations udf_symlink_aops = { + readpage: udf_symlink_filler, }; diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 4054da721..f5602dc0e 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -188,21 +188,10 @@ void udf_truncate(struct inode * inode) if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; - udf_trunc(inode); - - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); -} - -void udf_truncate_adinicb(struct inode * inode) -{ - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; - - UDF_I_LENALLOC(inode) = inode->i_size; + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + UDF_I_LENALLOC(inode) = inode->i_size; + else + udf_trunc(inode); inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 171cd0d75..39418f730 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -63,8 +63,8 @@ struct super_block; extern struct inode_operations udf_dir_inode_operations; extern struct inode_operations udf_file_inode_operations; -extern struct inode_operations udf_file_inode_operations_adinicb; -extern struct inode_operations udf_symlink_inode_operations; +extern struct address_space_operations udf_adinicb_aops; +extern struct address_space_operations udf_symlink_aops; struct udf_fileident_bh { @@ -137,9 +137,6 @@ extern int udf_sync_inode(struct inode *); extern void udf_expand_file_adinicb(struct file *, int, int *); extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *); extern struct buffer_head * udf_getblk(struct inode *, long, int, int *); -extern int udf_get_block(struct inode *, long, struct buffer_head *, int); -extern int udf_readpage_adinicb (struct dentry *, struct page *); -extern int udf_writepage_adinicb (struct dentry *, struct page *); extern struct buffer_head * udf_bread(struct inode *, int, int, int *); extern void udf_read_inode(struct inode *); extern void udf_put_inode(struct inode *); @@ -187,14 +184,12 @@ extern struct inode * udf_new_inode (const struct inode *, int, int *); /* truncate.c */ extern void udf_trunc(struct inode *); extern void udf_truncate(struct inode *); -extern void udf_truncate_adinicb(struct inode *); /* balloc.c */ extern void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32); extern int udf_alloc_blocks(const struct inode *, Uint16, Uint32, Uint32); extern int udf_new_block(const struct inode *, Uint16, Uint32, int *); extern int udf_sync_file(struct file *, struct dentry *); -extern int udf_sync_file_adinicb(struct file *, struct dentry *); /* directory.c */ extern Uint8 * udf_filead_read(struct inode *, Uint8 *, Uint8, lb_addr, int *, int *, struct buffer_head **, int *); diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index a828f3036..0e4e3a720 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -178,18 +178,8 @@ int ufs_check_dir_entry (const char * function, struct inode * dir, } static struct file_operations ufs_dir_operations = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - ufs_readdir, /* readdir */ - NULL, /* select */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ - file_fsync, /* fsync */ - NULL, /* fasync */ + readdir: ufs_readdir, + fsync: file_fsync, }; struct inode_operations ufs_dir_inode_operations = { @@ -205,9 +195,6 @@ struct inode_operations ufs_dir_inode_operations = { ufs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ NULL, /* truncate */ ufs_permission, /* permission */ NULL /* revalidate */ diff --git a/fs/ufs/file.c b/fs/ufs/file.c index fa4734d23..384774cc5 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -36,11 +36,6 @@ #include <linux/mm.h> #include <linux/pagemap.h> -#define NBUF 32 - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - /* * Make sure the offset never goes beyond the 32-bit mark.. */ @@ -72,86 +67,18 @@ static long long ufs_file_lseek( return retval; } -static inline void remove_suid(struct inode *inode) -{ - unsigned int mode; - - /* set S_IGID if S_IXGRP is set, and always set S_ISUID */ - mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID; - - /* was any of the uid bits set? */ - mode &= inode->i_mode; - if (mode && !suser()) { - inode->i_mode &= ~mode; - mark_inode_dirty(inode); - } -} - -/* - * Write to a file (through the page cache). - */ -static ssize_t -ufs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - ssize_t retval; - - retval = generic_file_write(file, buf, count, - ppos, block_write_partial_page); - if (retval > 0) { - struct inode *inode = file->f_dentry->d_inode; - remove_suid(inode); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - mark_inode_dirty(inode); - } - return retval; -} - -/* - * Called when an inode is released. Note that this is different - * from ufs_open: open gets called at every open, but release - * gets called only when /all/ the files are closed. - */ -static int ufs_release_file (struct inode * inode, struct file * filp) -{ - return 0; -} - /* * We have mostly NULL's here: the current defaults are ok for * the ufs filesystem. */ static struct file_operations ufs_file_operations = { - ufs_file_lseek, /* lseek */ - generic_file_read, /* read */ - ufs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - ufs_release_file, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ + llseek: ufs_file_lseek, + read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap, }; struct inode_operations ufs_file_inode_operations = { - &ufs_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - ufs_getfrag_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ - ufs_truncate, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ + &ufs_file_operations, + truncate: ufs_truncate, }; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 333186247..de2eec9a8 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -395,7 +395,7 @@ out: return result; } -int ufs_getfrag_block (struct inode *inode, long fragment, struct buffer_head *bh_result, int create) +static int ufs_getfrag_block (struct inode *inode, long fragment, struct buffer_head *bh_result, int create) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -540,6 +540,30 @@ struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment, return NULL; } +static int ufs_writepage(struct dentry *dentry, struct page *page) +{ + return block_write_full_page(page,ufs_getfrag_block); +} +static int ufs_readpage(struct dentry *dentry, struct page *page) +{ + return block_read_full_page(page,ufs_getfrag_block); +} +static int ufs_prepare_write(struct page *page, unsigned from, unsigned to) +{ + return block_prepare_write(page,from,to,ufs_getfrag_block); +} +static int ufs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,ufs_getfrag_block); +} +struct address_space_operations ufs_aops = { + readpage: ufs_readpage, + writepage: ufs_writepage, + prepare_write: ufs_prepare_write, + commit_write: generic_commit_write, + bmap: ufs_bmap +}; + void ufs_read_inode (struct inode * inode) { struct super_block * sb; @@ -619,15 +643,19 @@ void ufs_read_inode (struct inode * inode) inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &ufs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) + inode->i_mapping->a_ops = &ufs_aops; + } else if (S_ISDIR(inode->i_mode)) inode->i_op = &ufs_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = inode->i_blocks - ?&ufs_symlink_inode_operations - :&ufs_fast_symlink_inode_operations; - else + else if (S_ISLNK(inode->i_mode)) { + if (!inode->i_blocks) + inode->i_op = &ufs_fast_symlink_inode_operations; + else { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ufs_aops; + } + } else init_special_inode(inode, inode->i_mode, SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0])); diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 33413b8cd..71198abc4 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -424,6 +424,7 @@ int ufs_create (struct inode * dir, struct dentry * dentry, int mode) if (!inode) return err; inode->i_op = &ufs_file_inode_operations; + inode->i_mapping->a_ops = &ufs_aops; inode->i_mode = mode; mark_inode_dirty(inode); bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); @@ -776,7 +777,8 @@ int ufs_symlink (struct inode * dir, struct dentry * dentry, if (l > sb->u.ufs_sb.s_uspi->s_maxsymlinklen) { /* slow symlink */ - inode->i_op = &ufs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ufs_aops; err = block_symlink(inode, symname, l); if (err) goto out_no_entry; diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c index 06ee82740..4550f44cf 100644 --- a/fs/ufs/symlink.c +++ b/fs/ufs/symlink.c @@ -1,6 +1,8 @@ /* * linux/fs/ufs/symlink.c * + * Only fast symlinks left here - the rest is done by generic code. AV, 1999 + * * Copyright (C) 1998 * Daniel Pirkl <daniel.pirkl@emai.cz> * Charles University, Faculty of Mathematics and Physics @@ -24,7 +26,6 @@ */ #include <linux/fs.h> -#include <linux/ufs_fs.h> static int ufs_readlink(struct dentry *dentry, char *buffer, int buflen) { @@ -42,10 +43,3 @@ struct inode_operations ufs_fast_symlink_inode_operations = { readlink: ufs_readlink, follow_link: ufs_follow_link, }; - -struct inode_operations ufs_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - get_block: ufs_getfrag_block, - readpage: block_read_full_page -}; diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index 8856feba7..f1b7b3ed4 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -8,8 +8,7 @@ # Note 2: the CFLAGS definitions are now in the main makefile. O_TARGET := umsdos.o -O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o \ - rdir.o symlink.o emd.o check.o +O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o check.o M_OBJS := $(O_TARGET) diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index af875aa79..366657ae6 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -802,17 +802,9 @@ out_noread: static struct file_operations umsdos_dir_operations = { - NULL, /* lseek - default */ - dummy_dir_read, /* read */ - NULL, /* write - bad */ - UMSDOS_readdir, /* readdir */ - NULL, /* poll - default */ - UMSDOS_ioctl_dir, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* fsync */ + read: dummy_dir_read, + readdir: UMSDOS_readdir, + ioctl: UMSDOS_ioctl_dir, }; struct inode_operations umsdos_dir_inode_operations = @@ -827,12 +819,4 @@ struct inode_operations umsdos_dir_inode_operations = UMSDOS_rmdir, /* rmdir */ UMSDOS_mknod, /* mknod */ UMSDOS_rename, /* rename */ - NULL, /* readlink */ - NULL, /* followlink */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 252f8a48b..fb8dc03ff 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -155,7 +155,8 @@ dentry, f_pos)); } else if (S_ISDIR (inode->i_mode)) { umsdos_setup_dir(dentry); } else if (S_ISLNK (inode->i_mode)) { - inode->i_op = &umsdos_symlink_inode_operations; + /* address_space operations already set */ + inode->i_op = &page_symlink_inode_operations; } else init_special_inode(inode, inode->i_mode, kdev_t_to_nr(inode->i_rdev)); diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 2b972a524..a466a9396 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -220,17 +220,9 @@ out: */ static struct file_operations umsdos_rdir_operations = { - NULL, /* lseek - default */ - dummy_dir_read, /* read */ - NULL, /* write - bad */ - UMSDOS_rreaddir, /* readdir */ - NULL, /* poll - default */ - UMSDOS_ioctl_dir, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* fsync */ + read: dummy_dir_read, + readdir: UMSDOS_rreaddir, + ioctl: UMSDOS_ioctl_dir, }; struct inode_operations umsdos_rdir_inode_operations = @@ -245,12 +237,4 @@ struct inode_operations umsdos_rdir_inode_operations = UMSDOS_rrmdir, /* rmdir */ NULL, /* mknod */ msdos_rename, /* rename */ - NULL, /* readlink */ - NULL, /* followlink */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* get_block */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c deleted file mode 100644 index 7bcd09e44..000000000 --- a/fs/umsdos/symlink.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * linux/fs/umsdos/file.c - * - * Written 1992 by Jacques Gelinas - * inspired from linux/fs/msdos/file.c Werner Almesberger - * - * Extended MS-DOS regular file handling primitives - * - * Wow. It looks like we could support them on FAT with little (if any) - * problems. Oh, well... - */ - -#include <linux/fs.h> -#include <linux/msdos_fs.h> - -struct inode_operations umsdos_symlink_inode_operations = -{ - readlink: page_readlink, - follow_link: page_follow_link, - get_block: fat_get_block, - readpage: block_read_full_page -}; diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index 0dea64e75..0aced9465 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -1276,14 +1276,6 @@ struct inode_operations vfat_dir_inode_operations = { vfat_rmdir, /* rmdir */ NULL, /* mknod */ vfat_rename, /* rename */ - NULL, /* readlink */ - NULL, /* followlink */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* revalidate */ }; struct super_block *vfat_read_super(struct super_block *sb,void *data, |