diff options
Diffstat (limited to 'fs/adfs')
| -rw-r--r-- | fs/adfs/Kconfig | 4 | ||||
| -rw-r--r-- | fs/adfs/adfs.h | 40 | ||||
| -rw-r--r-- | fs/adfs/dir.c | 62 | ||||
| -rw-r--r-- | fs/adfs/dir_f.c | 23 | ||||
| -rw-r--r-- | fs/adfs/dir_fplus.c | 119 | ||||
| -rw-r--r-- | fs/adfs/file.c | 10 | ||||
| -rw-r--r-- | fs/adfs/inode.c | 102 | ||||
| -rw-r--r-- | fs/adfs/map.c | 2 | ||||
| -rw-r--r-- | fs/adfs/super.c | 90 |
9 files changed, 288 insertions, 164 deletions
diff --git a/fs/adfs/Kconfig b/fs/adfs/Kconfig index e55182a7460..c5a7787dd5e 100644 --- a/fs/adfs/Kconfig +++ b/fs/adfs/Kconfig @@ -1,6 +1,6 @@ config ADFS_FS - tristate "ADFS file system support (EXPERIMENTAL)" - depends on BLOCK && EXPERIMENTAL + tristate "ADFS file system support" + depends on BLOCK help The Acorn Disc Filing System is the standard file system of the RiscOS operating system which runs on Acorn's ARM-based Risc PC diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 9cc18775b83..c770337c4b4 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -43,13 +43,17 @@ struct adfs_dir_ops; * ADFS file system superblock data in memory */ struct adfs_sb_info { - struct adfs_discmap *s_map; /* bh list containing map */ - struct adfs_dir_ops *s_dir; /* directory operations */ - - uid_t s_uid; /* owner uid */ - gid_t s_gid; /* owner gid */ + union { struct { + struct adfs_discmap *s_map; /* bh list containing map */ + struct adfs_dir_ops *s_dir; /* directory operations */ + }; + struct rcu_head rcu; /* used only at shutdown time */ + }; + kuid_t s_uid; /* owner uid */ + kgid_t s_gid; /* owner gid */ umode_t s_owner_mask; /* ADFS owner perm -> unix perm */ umode_t s_other_mask; /* ADFS other perm -> unix perm */ + int s_ftsuffix; /* ,xyz hex filetype suffix option */ __u32 s_ids_per_zone; /* max. no ids in one zone */ __u32 s_idlen; /* length of ID in map */ @@ -79,6 +83,10 @@ struct adfs_dir { int nr_buffers; struct buffer_head *bh[4]; + + /* big directories need allocated buffers */ + struct buffer_head **bh_fplus; + unsigned int pos; unsigned int parent_id; @@ -89,7 +97,7 @@ struct adfs_dir { /* * This is the overall maximum name length */ -#define ADFS_MAX_NAME_LEN 256 +#define ADFS_MAX_NAME_LEN (256 + 4) /* +4 for ,xyz hex filetype suffix */ struct object_info { __u32 parent_id; /* parent object id */ __u32 file_id; /* object id */ @@ -97,10 +105,26 @@ struct object_info { __u32 execaddr; /* execution address */ __u32 size; /* size */ __u8 attr; /* RISC OS attributes */ - unsigned char name_len; /* name length */ + unsigned int name_len; /* name length */ char name[ADFS_MAX_NAME_LEN];/* file name */ + + /* RISC OS file type (12-bit: derived from loadaddr) */ + __u16 filetype; }; +/* RISC OS 12-bit filetype converts to ,xyz hex filename suffix */ +static inline int append_filetype_suffix(char *buf, __u16 filetype) +{ + if (filetype == 0xffff) /* no explicit 12-bit file type was set */ + return 0; + + *buf++ = ','; + *buf++ = hex_asc_lo(filetype >> 8); + *buf++ = hex_asc_lo(filetype >> 4); + *buf++ = hex_asc_lo(filetype >> 0); + return 4; +} + 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); @@ -121,7 +145,7 @@ struct adfs_discmap { /* Inode stuff */ struct inode *adfs_iget(struct super_block *sb, struct object_info *obj); -int adfs_write_inode(struct inode *inode,int unused); +int adfs_write_inode(struct inode *inode, struct writeback_control *wbc); int adfs_notify_change(struct dentry *dentry, struct iattr *attr); /* map.c */ diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 23aa52f548a..0d138c0de29 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -9,7 +9,6 @@ * * Common directory handling for ADFS */ -#include <linux/smp_lock.h> #include "adfs.h" /* @@ -18,49 +17,43 @@ static DEFINE_RWLOCK(adfs_dir_lock); static int -adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +adfs_readdir(struct file *file, struct dir_context *ctx) { - struct inode *inode = filp->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; struct object_info obj; struct adfs_dir dir; int ret = 0; - lock_kernel(); - - if (filp->f_pos >> 32) - goto out; + if (ctx->pos >> 32) + return 0; ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); if (ret) - goto out; + return ret; - switch ((unsigned long)filp->f_pos) { - case 0: - if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) + if (ctx->pos == 0) { + if (!dir_emit_dot(file, ctx)) goto free_out; - filp->f_pos += 1; - - case 1: - if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0) + ctx->pos = 1; + } + if (ctx->pos == 1) { + if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) goto free_out; - filp->f_pos += 1; - - default: - break; + ctx->pos = 2; } read_lock(&adfs_dir_lock); - ret = ops->setpos(&dir, filp->f_pos - 2); + ret = ops->setpos(&dir, ctx->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, DT_UNKNOWN) < 0) - goto unlock_out; - filp->f_pos += 1; + if (!dir_emit(ctx, obj.name, obj.name_len, + obj.file_id, DT_UNKNOWN)) + break; + ctx->pos++; } unlock_out: @@ -68,9 +61,6 @@ unlock_out: free_out: ops->free(&dir); - -out: - unlock_kernel(); return ret; } @@ -196,12 +186,12 @@ out: const struct file_operations adfs_dir_operations = { .read = generic_read_dir, .llseek = generic_file_llseek, - .readdir = adfs_readdir, - .fsync = simple_fsync, + .iterate = adfs_readdir, + .fsync = generic_file_fsync, }; static int -adfs_hash(struct dentry *parent, struct qstr *qstr) +adfs_hash(const struct dentry *parent, struct qstr *qstr) { const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen; const unsigned char *name; @@ -237,17 +227,18 @@ adfs_hash(struct dentry *parent, struct qstr *qstr) * requirements of the underlying filesystem. */ static int -adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name) +adfs_compare(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { int i; - if (entry->len != name->len) + if (len != name->len) return 1; for (i = 0; i < name->len; i++) { char a, b; - a = entry->name[i]; + a = str[i]; b = name->name[i]; if (a >= 'A' && a <= 'Z') @@ -267,14 +258,12 @@ const struct dentry_operations adfs_dentry_operations = { }; static struct dentry * -adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct inode *inode = NULL; struct object_info obj; int error; - dentry->d_op = &adfs_dentry_operations; - lock_kernel(); error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); if (error == 0) { error = -EACCES; @@ -286,7 +275,6 @@ adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) if (inode) error = 0; } - unlock_kernel(); d_add(dentry, inode); return ERR_PTR(error); } diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index bafc71222e2..4bbe853ee50 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -52,7 +52,6 @@ static inline int adfs_readname(char *buf, char *ptr, int maxlen) *buf++ = *ptr; ptr++; } - *buf = '\0'; return buf - old_buf; } @@ -208,7 +207,8 @@ release_buffers: * 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) +adfs_dir2obj(struct adfs_dir *dir, 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); @@ -216,6 +216,23 @@ adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de) obj->execaddr = adfs_readval(de->direxec, 4); obj->size = adfs_readval(de->dirlen, 4); obj->attr = de->newdiratts; + obj->filetype = -1; + + /* + * object is a file and is filetyped and timestamped? + * RISC OS 12-bit filetype is stored in load_address[19:8] + */ + if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && + (0xfff00000 == (0xfff00000 & obj->loadaddr))) { + obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); + + /* optionally append the ,xyz hex filetype suffix */ + if (ADFS_SB(dir->sb)->s_ftsuffix) + obj->name_len += + append_filetype_suffix( + &obj->name[obj->name_len], + obj->filetype); + } } /* @@ -260,7 +277,7 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj) if (!de.dirobname[0]) return -ENOENT; - adfs_dir2obj(obj, &de); + adfs_dir2obj(dir, obj, &de); return 0; } diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 1796bb352d0..d9e3bee4e65 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ #include <linux/buffer_head.h> +#include <linux/slab.h> #include "adfs.h" #include "dir_fplus.h" @@ -22,30 +23,53 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct dir->nr_buffers = 0; + /* start off using fixed bh set - only alloc for big dirs */ + dir->bh_fplus = &dir->bh[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] = sb_bread(sb, block); - if (!dir->bh[0]) + dir->bh_fplus[0] = sb_bread(sb, block); + if (!dir->bh_fplus[0]) goto out; dir->nr_buffers += 1; - h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + h = (struct adfs_bigdirheader *)dir->bh_fplus[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"); + printk(KERN_WARNING "adfs: adfs_fplus_read:" + " directory header size %X\n" + " does not match directory size %X\n", + size, sz); } if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 || h->bigdirversion[2] != 0 || size & 2047 || - h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) + h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) { + printk(KERN_WARNING "adfs: dir object %X has" + " malformed dir header\n", id); goto out; + } size >>= sb->s_blocksize_bits; + if (size > sizeof(dir->bh)/sizeof(dir->bh[0])) { + /* this directory is too big for fixed bh set, must allocate */ + struct buffer_head **bh_fplus = + kzalloc(size * sizeof(struct buffer_head *), + GFP_KERNEL); + if (!bh_fplus) { + adfs_error(sb, "not enough memory for" + " dir object %X (%d blocks)", id, size); + goto out; + } + dir->bh_fplus = bh_fplus; + /* copy over the pointer to the block that we've already read */ + dir->bh_fplus[0] = dir->bh[0]; + } + for (blk = 1; blk < size; blk++) { block = __adfs_block_map(sb, id, blk); if (!block) { @@ -53,25 +77,44 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct goto out; } - dir->bh[blk] = sb_bread(sb, block); - if (!dir->bh[blk]) + dir->bh_fplus[blk] = sb_bread(sb, block); + if (!dir->bh_fplus[blk]) { + adfs_error(sb, "dir object %X failed read for" + " offset %d, mapped block %X", + id, blk, block); goto out; - dir->nr_buffers = blk; + } + + dir->nr_buffers += 1; } - t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8)); + t = (struct adfs_bigdirtail *) + (dir->bh_fplus[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) + t->reserved[0] != 0 || t->reserved[1] != 0) { + printk(KERN_WARNING "adfs: dir object %X has " + "malformed dir end\n", id); 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]); + if (dir->bh_fplus) { + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bh_fplus[i]); + + if (&dir->bh[0] != dir->bh_fplus) + kfree(dir->bh_fplus); + + dir->bh_fplus = NULL; + } + + dir->nr_buffers = 0; dir->sb = NULL; return ret; } @@ -79,7 +122,8 @@ out: static int adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) { - struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + struct adfs_bigdirheader *h = + (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data; int ret = -ENOENT; if (fpos <= le32_to_cpu(h->bigdirentries)) { @@ -102,21 +146,27 @@ dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len) partial = sb->s_blocksize - offset; if (partial >= len) - memcpy(to, dir->bh[buffer]->b_data + offset, len); + memcpy(to, dir->bh_fplus[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); + memcpy(c, + dir->bh_fplus[buffer]->b_data + offset, + partial); + + memcpy(c + partial, + dir->bh_fplus[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_bigdirheader *h = + (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data; struct adfs_bigdirentry bde; unsigned int offset; int i, ret = -ENOENT; @@ -147,6 +197,24 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) if (obj->name[i] == '/') obj->name[i] = '.'; + obj->filetype = -1; + + /* + * object is a file and is filetyped and timestamped? + * RISC OS 12-bit filetype is stored in load_address[19:8] + */ + if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && + (0xfff00000 == (0xfff00000 & obj->loadaddr))) { + obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); + + /* optionally append the ,xyz hex filetype suffix */ + if (ADFS_SB(dir->sb)->s_ftsuffix) + obj->name_len += + append_filetype_suffix( + &obj->name[obj->name_len], + obj->filetype); + } + dir->pos += 1; ret = 0; out: @@ -160,7 +228,7 @@ adfs_fplus_sync(struct adfs_dir *dir) int i; for (i = dir->nr_buffers - 1; i >= 0; i--) { - struct buffer_head *bh = dir->bh[i]; + struct buffer_head *bh = dir->bh_fplus[i]; sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) err = -EIO; @@ -174,8 +242,17 @@ adfs_fplus_free(struct adfs_dir *dir) { int i; - for (i = 0; i < dir->nr_buffers; i++) - brelse(dir->bh[i]); + if (dir->bh_fplus) { + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bh_fplus[i]); + + if (&dir->bh[0] != dir->bh_fplus) + kfree(dir->bh_fplus); + + dir->bh_fplus = NULL; + } + + dir->nr_buffers = 0; dir->sb = NULL; } diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 005ea34d175..07c9edce5aa 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -23,12 +23,12 @@ const struct file_operations adfs_file_operations = { .llseek = generic_file_llseek, - .read = do_sync_read, - .aio_read = generic_file_aio_read, + .read = new_sync_read, + .read_iter = generic_file_read_iter, .mmap = generic_file_mmap, - .fsync = simple_fsync, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .fsync = generic_file_fsync, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .splice_read = generic_file_splice_read, }; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 3f57ce4bee5..b9acadafa4a 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -7,8 +7,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include <linux/smp_lock.h> #include <linux/buffer_head.h> +#include <linux/writeback.h> #include "adfs.h" /* @@ -45,14 +45,28 @@ static int adfs_readpage(struct file *file, struct page *page) return block_read_full_page(page, adfs_get_block); } +static void adfs_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + + if (to > inode->i_size) + truncate_pagecache(inode, inode->i_size); +} + static int adfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, adfs_get_block, &ADFS_I(mapping->host)->mmu_private); + if (unlikely(ret)) + adfs_write_failed(mapping, pos + len); + + return ret; } static sector_t _adfs_bmap(struct address_space *mapping, sector_t block) @@ -63,32 +77,18 @@ static sector_t _adfs_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations adfs_aops = { .readpage = adfs_readpage, .writepage = adfs_writepage, - .sync_page = block_sync_page, .write_begin = adfs_write_begin, .write_end = generic_write_end, .bmap = _adfs_bmap }; -static inline unsigned int -adfs_filetype(struct inode *inode) -{ - unsigned int type; - - if (ADFS_I(inode)->stamped) - type = (ADFS_I(inode)->loadaddr >> 8) & 0xfff; - else - type = (unsigned int) -1; - - return type; -} - /* * Convert ADFS attributes and filetype to Linux permission. */ static umode_t adfs_atts2mode(struct super_block *sb, struct inode *inode) { - unsigned int filetype, attr = ADFS_I(inode)->attr; + unsigned int attr = ADFS_I(inode)->attr; umode_t mode, rmask; struct adfs_sb_info *asb = ADFS_SB(sb); @@ -97,9 +97,7 @@ adfs_atts2mode(struct super_block *sb, struct inode *inode) return S_IFDIR | S_IXUGO | mode; } - filetype = adfs_filetype(inode); - - switch (filetype) { + switch (ADFS_I(inode)->filetype) { case 0xfc0: /* LinkFS */ return S_IFLNK|S_IRWXUGO; @@ -165,50 +163,48 @@ adfs_mode2atts(struct super_block *sb, struct inode *inode) /* * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time - * referenced to 1 Jan 1900 (til 2248) + * referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds + * of time to convert from RISC OS epoch to Unix epoch. */ static void adfs_adfs2unix_time(struct timespec *tv, struct inode *inode) { unsigned int high, low; + /* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since + * 01 Jan 1900 00:00:00 (RISC OS epoch) + */ + static const s64 nsec_unix_epoch_diff_risc_os_epoch = + 2208988800000000000LL; + s64 nsec; if (ADFS_I(inode)->stamped == 0) goto cur_time; - high = ADFS_I(inode)->loadaddr << 24; - low = ADFS_I(inode)->execaddr; + high = ADFS_I(inode)->loadaddr & 0xFF; /* top 8 bits of timestamp */ + low = ADFS_I(inode)->execaddr; /* bottom 32 bits of timestamp */ - high |= low >> 8; - low &= 255; + /* convert 40-bit centi-seconds to 32-bit seconds + * going via nanoseconds to retain precision + */ + nsec = (((s64) high << 32) | (s64) low) * 10000000; /* cs to ns */ /* Files dated pre 01 Jan 1970 00:00:00. */ - if (high < 0x336e996a) + if (nsec < nsec_unix_epoch_diff_risc_os_epoch) goto too_early; - /* Files dated post 18 Jan 2038 03:14:05. */ - if (high >= 0x656e9969) - goto too_late; - - /* discard 2208988800 (0x336e996a00) seconds of time */ - high -= 0x336e996a; + /* convert from RISC OS to Unix epoch */ + nsec -= nsec_unix_epoch_diff_risc_os_epoch; - /* convert 40-bit centi-seconds to 32-bit seconds */ - tv->tv_sec = (((high % 100) << 8) + low) / 100 + (high / 100 << 8); - tv->tv_nsec = 0; + *tv = ns_to_timespec(nsec); return; cur_time: - *tv = CURRENT_TIME_SEC; + *tv = CURRENT_TIME; return; too_early: tv->tv_sec = tv->tv_nsec = 0; return; - - too_late: - tv->tv_sec = 0x7ffffffd; - tv->tv_nsec = 0; - return; } /* @@ -256,7 +252,7 @@ adfs_iget(struct super_block *sb, struct object_info *obj) inode->i_gid = ADFS_SB(sb)->s_gid; inode->i_ino = obj->file_id; inode->i_size = obj->size; - inode->i_nlink = 2; + set_nlink(inode, 2); inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; @@ -270,7 +266,8 @@ adfs_iget(struct super_block *sb, struct object_info *obj) ADFS_I(inode)->loadaddr = obj->loadaddr; ADFS_I(inode)->execaddr = obj->execaddr; ADFS_I(inode)->attr = obj->attr; - ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000); + ADFS_I(inode)->filetype = obj->filetype; + ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000); inode->i_mode = adfs_atts2mode(sb, inode); adfs_adfs2unix_time(&inode->i_mtime, inode); @@ -306,26 +303,22 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr) unsigned int ia_valid = attr->ia_valid; int error; - lock_kernel(); - 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 != ADFS_SB(sb)->s_uid) || - (ia_valid & ATTR_GID && attr->ia_gid != ADFS_SB(sb)->s_gid)) + if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, ADFS_SB(sb)->s_uid)) || + (ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, ADFS_SB(sb)->s_gid))) error = -EPERM; if (error) goto out; + /* XXX: this is missing some actual on-disk truncation.. */ if (ia_valid & ATTR_SIZE) - error = vmtruncate(inode, attr->ia_size); - - if (error) - goto out; + truncate_setsize(inode, attr->ia_size); if (ia_valid & ATTR_MTIME) { inode->i_mtime = attr->ia_mtime; @@ -351,7 +344,6 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr) if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE)) mark_inode_dirty(inode); out: - unlock_kernel(); return error; } @@ -360,13 +352,12 @@ out: * The adfs-specific inode data has already been updated by * adfs_notify_change() */ -int adfs_write_inode(struct inode *inode, int wait) +int adfs_write_inode(struct inode *inode, struct writeback_control *wbc) { struct super_block *sb = inode->i_sb; struct object_info obj; int ret; - lock_kernel(); obj.file_id = inode->i_ino; obj.name_len = 0; obj.parent_id = ADFS_I(inode)->parent_id; @@ -375,7 +366,6 @@ int adfs_write_inode(struct inode *inode, int wait) obj.attr = ADFS_I(inode)->attr; obj.size = inode->i_size; - ret = adfs_dir_update(sb, &obj, wait); - unlock_kernel(); + ret = adfs_dir_update(sb, &obj, wbc->sync_mode == WB_SYNC_ALL); return ret; } diff --git a/fs/adfs/map.c b/fs/adfs/map.c index d1a5932bb0f..6935f05202a 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -51,7 +51,7 @@ static DEFINE_RWLOCK(adfs_map_lock); /* * This is fun. We need to load up to 19 bits from the map at an - * arbitary bit alignment. (We're limited to 19 bits by F+ version 2). + * arbitrary bit alignment. (We're limited to 19 bits by F+ version 2). */ #define GET_FRAG_ID(_map,_start,_idmask) \ ({ \ diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 6910a98bd73..9852bdf34d7 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -13,8 +13,9 @@ #include <linux/parser.h> #include <linux/mount.h> #include <linux/seq_file.h> -#include <linux/smp_lock.h> +#include <linux/slab.h> #include <linux/statfs.h> +#include <linux/user_namespace.h> #include "adfs.h" #include "dir_f.h" #include "dir_fplus.h" @@ -119,40 +120,38 @@ static void adfs_put_super(struct super_block *sb) int i; struct adfs_sb_info *asb = ADFS_SB(sb); - lock_kernel(); - for (i = 0; i < asb->s_map_size; i++) brelse(asb->s_map[i].dm_bh); kfree(asb->s_map); - kfree(asb); - sb->s_fs_info = NULL; - - unlock_kernel(); + kfree_rcu(asb, rcu); } -static int adfs_show_options(struct seq_file *seq, struct vfsmount *mnt) +static int adfs_show_options(struct seq_file *seq, struct dentry *root) { - struct adfs_sb_info *asb = ADFS_SB(mnt->mnt_sb); + struct adfs_sb_info *asb = ADFS_SB(root->d_sb); - if (asb->s_uid != 0) - seq_printf(seq, ",uid=%u", asb->s_uid); - if (asb->s_gid != 0) - seq_printf(seq, ",gid=%u", asb->s_gid); + if (!uid_eq(asb->s_uid, GLOBAL_ROOT_UID)) + seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, asb->s_uid)); + if (!gid_eq(asb->s_gid, GLOBAL_ROOT_GID)) + seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, asb->s_gid)); if (asb->s_owner_mask != ADFS_DEFAULT_OWNER_MASK) seq_printf(seq, ",ownmask=%o", asb->s_owner_mask); if (asb->s_other_mask != ADFS_DEFAULT_OTHER_MASK) seq_printf(seq, ",othmask=%o", asb->s_other_mask); + if (asb->s_ftsuffix != 0) + seq_printf(seq, ",ftsuffix=%u", asb->s_ftsuffix); return 0; } -enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err}; +enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_ftsuffix, Opt_err}; static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_ownmask, "ownmask=%o"}, {Opt_othmask, "othmask=%o"}, + {Opt_ftsuffix, "ftsuffix=%u"}, {Opt_err, NULL} }; @@ -176,12 +175,16 @@ static int parse_options(struct super_block *sb, char *options) case Opt_uid: if (match_int(args, &option)) return -EINVAL; - asb->s_uid = option; + asb->s_uid = make_kuid(current_user_ns(), option); + if (!uid_valid(asb->s_uid)) + return -EINVAL; break; case Opt_gid: if (match_int(args, &option)) return -EINVAL; - asb->s_gid = option; + asb->s_gid = make_kgid(current_user_ns(), option); + if (!gid_valid(asb->s_gid)) + return -EINVAL; break; case Opt_ownmask: if (match_octal(args, &option)) @@ -193,6 +196,11 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; asb->s_other_mask = option; break; + case Opt_ftsuffix: + if (match_int(args, &option)) + return -EINVAL; + asb->s_ftsuffix = option; + break; default: printk("ADFS-fs: unrecognised mount option \"%s\" " "or missing value\n", p); @@ -204,6 +212,7 @@ static int parse_options(struct super_block *sb, char *options) static int adfs_remount(struct super_block *sb, int *flags, char *data) { + sync_filesystem(sb); *flags |= MS_NODIRATIME; return parse_options(sb, data); } @@ -239,11 +248,17 @@ static struct inode *adfs_alloc_inode(struct super_block *sb) return &ei->vfs_inode; } -static void adfs_destroy_inode(struct inode *inode) +static void adfs_i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(adfs_inode_cachep, ADFS_I(inode)); } +static void adfs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, adfs_i_callback); +} + static void init_once(void *foo) { struct adfs_inode_info *ei = (struct adfs_inode_info *) foo; @@ -251,7 +266,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { adfs_inode_cachep = kmem_cache_create("adfs_inode_cache", sizeof(struct adfs_inode_info), @@ -265,6 +280,11 @@ static int init_inodecache(void) static void destroy_inodecache(void) { + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); kmem_cache_destroy(adfs_inode_cachep); } @@ -359,10 +379,11 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = asb; /* set default options */ - asb->s_uid = 0; - asb->s_gid = 0; + asb->s_uid = GLOBAL_ROOT_UID; + asb->s_gid = GLOBAL_ROOT_GID; asb->s_owner_mask = ADFS_DEFAULT_OWNER_MASK; asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK; + asb->s_ftsuffix = 0; if (parse_options(sb, data)) goto error; @@ -442,11 +463,13 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) 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; + /* Set root object date as 01 Jan 1987 00:00:00 */ + root_obj.loadaddr = 0xfff0003f; + root_obj.execaddr = 0xec22c000; root_obj.size = ADFS_NEWDIR_SIZE; root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ | ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; + root_obj.filetype = -1; /* * If this is a F+ disk with variable length directories, @@ -460,19 +483,24 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) asb->s_dir = &adfs_f_dir_ops; asb->s_namelen = ADFS_F_NAME_LEN; } + /* + * ,xyz hex filetype suffix may be added by driver + * to files that have valid RISC OS filetype + */ + if (asb->s_ftsuffix) + asb->s_namelen += 4; + sb->s_d_op = &adfs_dentry_operations; root = adfs_iget(sb, &root_obj); - sb->s_root = d_alloc_root(root); + sb->s_root = d_make_root(root); if (!sb->s_root) { int i; - iput(root); for (i = 0; i < asb->s_map_size; i++) brelse(asb->s_map[i].dm_bh); kfree(asb->s_map); adfs_error(sb, "get root inode failed\n"); goto error; - } else - sb->s_root->d_op = &adfs_dentry_operations; + } return 0; error_free_bh: @@ -483,20 +511,20 @@ error: return -EINVAL; } -static int adfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) +static struct dentry *adfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - return get_sb_bdev(fs_type, flags, dev_name, data, adfs_fill_super, - mnt); + return mount_bdev(fs_type, flags, dev_name, data, adfs_fill_super); } static struct file_system_type adfs_fs_type = { .owner = THIS_MODULE, .name = "adfs", - .get_sb = adfs_get_sb, + .mount = adfs_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; +MODULE_ALIAS_FS("adfs"); static int __init init_adfs_fs(void) { |
