diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-09-19 19:15:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-09-19 19:15:08 +0000 |
commit | 03ba4131783cc9e872f8bb26a03f15bc11f27564 (patch) | |
tree | 88db8dba75ae06ba3bad08e42c5e52efc162535c /fs/qnx4 | |
parent | 257730f99381dd26e10b832fce4c94cae7ac1176 (diff) |
- Merge with Linux 2.1.121.
- Bugfixes.
Diffstat (limited to 'fs/qnx4')
-rw-r--r-- | fs/qnx4/.cvsignore | 2 | ||||
-rw-r--r-- | fs/qnx4/BUGS | 21 | ||||
-rw-r--r-- | fs/qnx4/Makefile | 15 | ||||
-rw-r--r-- | fs/qnx4/README | 9 | ||||
-rw-r--r-- | fs/qnx4/TODO | 31 | ||||
-rw-r--r-- | fs/qnx4/bitmap.c | 196 | ||||
-rw-r--r-- | fs/qnx4/dir.c | 126 | ||||
-rw-r--r-- | fs/qnx4/file.c | 265 | ||||
-rw-r--r-- | fs/qnx4/fsync.c | 166 | ||||
-rw-r--r-- | fs/qnx4/inode.c | 464 | ||||
-rw-r--r-- | fs/qnx4/namei.c | 279 | ||||
-rw-r--r-- | fs/qnx4/symlinks.c | 121 | ||||
-rw-r--r-- | fs/qnx4/truncate.c | 38 |
13 files changed, 1733 insertions, 0 deletions
diff --git a/fs/qnx4/.cvsignore b/fs/qnx4/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/qnx4/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/qnx4/BUGS b/fs/qnx4/BUGS new file mode 100644 index 000000000..64d81b4bb --- /dev/null +++ b/fs/qnx4/BUGS @@ -0,0 +1,21 @@ +Last update: 03-07-1998 + +- Files in a subdir can't be accessed, I think that the inode information + is not correctly copied at some point. Solved 06-06-1998, Richard. + +- At some point the mounted device can't be unmounted. I think that somewhere + in the code a buffer is not given free. + +- Make the '..' entry work, I give it a great chance that the above bug + (not given free) has something to do with this one, after a 'ls -l' + the mounted device can't be unmounted and that's where the '..' entry + is accessed. + Seems to be solved 21-06-1998, Frank. + +- File read function not correct, after the first block it goes beserk. + Solved 21-06-1998, Frank. + +- This fs will not work if not built as a module. + Solved 25-06-1998, Frank. + +- Write/truncate/delete functions don't update the bitmap. diff --git a/fs/qnx4/Makefile b/fs/qnx4/Makefile new file mode 100644 index 000000000..1988bb8ff --- /dev/null +++ b/fs/qnx4/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the linux qnx4-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# 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 +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/fs/qnx4/README b/fs/qnx4/README new file mode 100644 index 000000000..e7300730b --- /dev/null +++ b/fs/qnx4/README @@ -0,0 +1,9 @@ + + This is a snapshot of the QNX4 filesystem for Linux. + Please send diffs and remarks to <qnxfs@rtc-one.net> . + +Credits : + +Richard "Scuba" A. Frowijn <scuba@wxs.nl> +Frank "Jedi/Sector One" Denis <j@4u.net> + diff --git a/fs/qnx4/TODO b/fs/qnx4/TODO new file mode 100644 index 000000000..fab3623f8 --- /dev/null +++ b/fs/qnx4/TODO @@ -0,0 +1,31 @@ +Name : QNX4 TODO list +Last update: 29-06-1998 + + - qnx4_checkroot (inode.c), currently there's a look for the '/' in + the root direntry, if so then the current mounted device is a qnx4 + partition. This has to be rewritten with a look for 'QNX4' in the + bootblock, it seems to me the savest way to ensure that the mounted + device is in fact a QNX4 partition. + Done 20-06-1998, Frank. But some disks (like QNX install floppies) + don't have 'QNX4' in their bootblock. + + - Bitmap functions. To find out the free space, largest free block, etc. + Partly done (RO), Richard, 05/06/1998. Optimized 20-06-1998, Frank. + + - Symbolic links. symlinks.c have to be rewritten. + + - Extended files. + + - Complete write, unlink and truncate functions : the bitmap should be +updated. + + - Porting to linux 2.1.99+ with dcache support. 20-06-1998, Frank. + + - Don't rewrite the file_read function : use the generic_file_read hook, + and write readpage instead. Done on 21-06-1998, Frank. + + - Write dinit and dcheck. + + - Solving the bugs. + + - Use le32_to_cpu and vice-versa for portability. diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c new file mode 100644 index 000000000..b73f36deb --- /dev/null +++ b/fs/qnx4/bitmap.c @@ -0,0 +1,196 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 28-05-1998 by Richard Frowijn : first release. + * 20-06-1998 by Frank Denis : basic optimisations. + * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap . + * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) . + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/qnx4_fs.h> +#include <linux/stat.h> +#include <linux/kernel.h> +#include <linux/string.h> + +#include <asm/bitops.h> + +int qnx4_new_block(struct super_block *sb) +{ + return 0; +} + +void count_bits(const register char *bmPart, register int size, + int *const tf) +{ + char b; + int tot = *tf; + + if (size > QNX4_BLOCK_SIZE) { + size = QNX4_BLOCK_SIZE; + } + do { + b = *bmPart++; + if ((b & 1) == 0) + tot++; + if ((b & 2) == 0) + tot++; + if ((b & 4) == 0) + tot++; + if ((b & 8) == 0) + tot++; + if ((b & 16) == 0) + tot++; + if ((b & 32) == 0) + tot++; + if ((b & 64) == 0) + tot++; + if ((b & 128) == 0) + tot++; + size--; + } while (size != 0); + *tf = tot; +} + +unsigned long qnx4_count_free_blocks(struct super_block *sb) +{ + int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int total = 0; + int total_free = 0; + int offset = 0; + int size = sb->u.qnx4_sb.BitMap->di_size; + struct buffer_head *bh; + + while (total < size) { + if ((bh = bread(sb->s_dev, start + offset, QNX4_BLOCK_SIZE)) == NULL) { + printk("qnx4: I/O error in counting free blocks\n"); + break; + } + count_bits(bh->b_data, size - total, &total_free); + brelse(bh); + total += QNX4_BLOCK_SIZE; + } + + return total_free; +} + +unsigned long qnx4_count_free_inodes(struct super_block *sb) +{ + return qnx4_count_free_blocks(sb) * QNX4_INODES_PER_BLOCK; /* FIXME */ +} + +int qnx4_is_free(struct super_block *sb, int block) +{ + int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int size = sb->u.qnx4_sb.BitMap->di_size; + struct buffer_head *bh; + const char *g; + int ret = -EIO; + + start += block / (QNX4_BLOCK_SIZE * 8); + QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n", + (unsigned long) block, (unsigned long) start)); + (void) size; /* CHECKME */ + bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE); + if (bh == NULL) { + return -EIO; + } + g = bh->b_data + (block % QNX4_BLOCK_SIZE); + if (((*g) & (1 << (block % 8))) == 0) { + QNX4DEBUG(("qnx4: is_free -> block is free\n")); + ret = 1; + } else { + QNX4DEBUG(("qnx4: is_free -> block is busy\n")); + ret = 0; + } + brelse(bh); + + return ret; +} + +int qnx4_bmap(struct inode *inode, int block) +{ + QNX4DEBUG(("qnx4: bmap on block [%d]\n", block)); + if (block < 0) { + return 0; + } + return !qnx4_is_free(inode->i_sb, block); +} + +#ifdef CONFIG_QNX4FS_RW + +int qnx4_set_bitmap(struct super_block *sb, int block, int busy) +{ + int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int size = sb->u.qnx4_sb.BitMap->di_size; + struct buffer_head *bh; + char *g; + + start += block / (QNX4_BLOCK_SIZE * 8); + QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n", + (unsigned long) block, (unsigned long) start)); + (void) size; /* CHECKME */ + bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE); + if (bh == NULL) { + return -EIO; + } + g = bh->b_data + (block % QNX4_BLOCK_SIZE); + if (busy == 0) { + (*g) &= ~(1 << (block % 8)); + } else { + (*g) |= (1 << (block % 8)); + } + mark_buffer_dirty(bh, 1); + brelse(bh); + + return 0; +} + +static void qnx4_clear_inode(struct inode *inode) +{ + struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i; + + memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved); + qnx4_ino->i_size = 0; + qnx4_ino->i_num_xtnts = 0; + qnx4_ino->i_mode = 0; + qnx4_ino->i_status = 0; +} + +void qnx4_free_inode(struct inode *inode) +{ + if (!inode) { + return; + } + if (!inode->i_dev) { + printk("free_inode: inode has no device\n"); + return; + } + if (inode->i_count > 1) { + printk("free_inode: inode has count=%d\n", inode->i_count); + return; + } + if (inode->i_nlink) { + printk("free_inode: inode has nlink=%d\n", inode->i_nlink); + return; + } + if (!inode->i_sb) { + printk("free_inode: inode on nonexistent device\n"); + return; + } + if (inode->i_ino < 1) { + printk("free_inode: inode 0 or nonexistent inode\n"); + return; + } + qnx4_clear_inode(inode); + clear_inode(inode); +} + +#endif diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c new file mode 100644 index 000000000..119930667 --- /dev/null +++ b/fs/qnx4/dir.c @@ -0,0 +1,126 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 28-05-1998 by Richard Frowijn : first release. + * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. + */ + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/qnx4_fs.h> +#include <linux/stat.h> + +#include <asm/segment.h> + +static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir); + +static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + unsigned int offset; + struct buffer_head *bh; + struct qnx4_inode_entry *de; + long blknum; + int i; + int size; + + blknum = inode->u.qnx4_i.i_first_xtnt.xtnt_blk - 1 + + ((filp->f_pos >> 6) >> 3); + + if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) { + return -EBADF; + } + QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); + QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos)); + QNX4DEBUG(("BlkNum = %ld\n", (long) blknum)); + + while (filp->f_pos < inode->i_size) { + bh = bread(inode->i_dev, blknum, QNX4_BLOCK_SIZE); + i = (filp->f_pos - (((filp->f_pos >> 6) >> 3) << 9)) & 0x3f; + while (i < QNX4_INODES_PER_BLOCK) { + offset = i * QNX4_DIR_ENTRY_SIZE; + de = (struct qnx4_inode_entry *) (bh->b_data + offset); + size = strlen(de->di_fname); + if (size) { + + QNX4DEBUG(("qnx4_readdir:%s\n", de->di_fname)); + + if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) { + if (de->di_status) { + if (filldir(dirent, de->di_fname, size, filp->f_pos, de->di_first_xtnt.xtnt_blk) < 0) { + brelse(bh); + return 0; + } + } + } + } + i++; + filp->f_pos += QNX4_DIR_ENTRY_SIZE; + } + brelse(bh); + blknum++; + } + UPDATE_ATIME(inode); + + return 0; +} + +static struct file_operations qnx4_dir_operations = +{ + NULL, /* lseek - default */ + NULL, /* read */ + NULL, /* write - bad */ + qnx4_readdir, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special flush code */ + NULL, /* no special release code */ + file_fsync, /* default fsync */ + NULL, /* default fasync */ + NULL, /* default check_media_change */ + NULL, /* default revalidate */ +}; + +struct inode_operations qnx4_dir_inode_operations = +{ + &qnx4_dir_operations, +#ifdef CONFIG_QNX4FS_RW + qnx4_create, +#else + NULL, /* create */ +#endif + qnx4_lookup, + NULL, /* link */ +#ifdef CONFIG_QNX4FS_RW + qnx4_unlink, /* unlink */ +#else + NULL, +#endif + NULL, /* symlink */ + NULL, /* mkdir */ +#ifdef CONFIG_QNX4FS_RW + qnx4_rmdir, /* rmdir */ +#else + NULL, +#endif + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c new file mode 100644 index 000000000..f26733d9a --- /dev/null +++ b/fs/qnx4/file.c @@ -0,0 +1,265 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 25-05-1998 by Richard Frowijn : first release. + * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read. + * 27-06-1998 by Frank Denis : file overwriting. + */ + +#include <linux/config.h> +#include <linux/types.h> +#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)) + +#include <linux/fs.h> +#include <linux/qnx4_fs.h> + +static int qnx4_readpage(struct file *file, struct page *page); + +#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 moostly NULL's here: the current defaults are ok for + * the qnx4 filesystem. + */ +static struct file_operations qnx4_file_operations = +{ + NULL, /* lseek - default */ + generic_file_read, /* read */ +#ifdef CONFIG_QNX4FS_RW + qnx4_file_write, /* write */ +#else + NULL, +#endif + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* no special flush code */ + NULL, /* release */ +#ifdef CONFIG_QNX4FS_RW + qnx4_sync_file, /* fsync */ +#else + NULL, +#endif + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations qnx4_file_inode_operations = +{ + &qnx4_file_operations, /* default file operations */ +#ifdef CONFIG_QNX4FS_RW + qnx4_create, /* create */ +#else + NULL, +#endif + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + qnx4_readpage, /* readpage */ + NULL, /* writepage */ + qnx4_bmap, /* bmap */ +#ifdef CONFIG_QNX4FS_RW + qnx4_truncate, /* truncate */ +#else + NULL, +#endif + NULL, /* permission */ + NULL /* smap */ +}; + +static int qnx4_readpage(struct file *file, struct page *page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i; + unsigned long buf; + unsigned long offset, avail, readlen; + unsigned long start; + unsigned long count; + struct buffer_head *bh; + int res = -EIO; + + QNX4DEBUG(("qnx4: readpage offset=[%ld]\n", (long) page->offset)); + + if (qnx4_ino->i_xblk != 0) { + printk("qnx4: sorry, this file is extended, don't know how to handle it (yet) !\n"); + return -EIO; + } + atomic_inc(&page->count); + set_bit(PG_locked, &page->flags); + buf = page_address(page); + clear_bit(PG_uptodate, &page->flags); + clear_bit(PG_error, &page->flags); + offset = page->offset; + + if (offset < inode->i_size) { + res = 0; + avail = inode->i_size - offset; + readlen = MIN(avail, PAGE_SIZE); + start = qnx4_ino->i_first_xtnt.xtnt_blk + (offset >> 9) - 1; + count = PAGE_SIZE / QNX4_BLOCK_SIZE; + do { + QNX4DEBUG(("qnx4: reading page starting at [%ld]\n", (long) start)); + if ((bh = bread(inode->i_dev, start, QNX4_BLOCK_SIZE)) == NULL) { + printk("qnx4: data corrupted or I/O error.\n"); + res = -EIO; + } else { + memcpy((void *) buf, bh->b_data, QNX4_BLOCK_SIZE); + } + buf += QNX4_BLOCK_SIZE; + start++; + count--; + } while (count != 0); + } + if (res != 0) { + set_bit(PG_error, &page->flags); + memset((void *) buf, 0, PAGE_SIZE); + } else { + set_bit(PG_uptodate, &page->flags); + } + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); +/* free_page(buf); */ + + return res; +} diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c new file mode 100644 index 000000000..826ee8a09 --- /dev/null +++ b/fs/qnx4/fsync.c @@ -0,0 +1,166 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 24-03-1998 by Richard Frowijn : first release. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/locks.h> + +#include <linux/fs.h> +#include <linux/qnx4_fs.h> + +#include <asm/segment.h> +#include <asm/system.h> + +#define blocksize QNX4_BLOCK_SIZE + +/* + * The functions for qnx4 fs file synchronization. + */ + +#ifdef CONFIG_QNX4FS_RW + +static int sync_block(struct inode *inode, unsigned short *block, int wait) +{ + struct buffer_head *bh; + unsigned short tmp; + + if (!*block) + return 0; + tmp = *block; + bh = get_hash_table(inode->i_dev, *block, blocksize); + if (!bh) + return 0; + if (*block != tmp) { + brelse(bh); + return 1; + } + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + brelse(bh); + return -1; + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + brelse(bh); + return 0; + } + ll_rw_block(WRITE, 1, &bh); + bh->b_count--; + return 0; +} + +static int sync_iblock(struct inode *inode, unsigned short *iblock, + struct buffer_head **bh, int wait) +{ + int rc; + unsigned short tmp; + + *bh = NULL; + tmp = *iblock; + if (!tmp) + return 0; + rc = sync_block(inode, iblock, wait); + if (rc) + return rc; + *bh = bread(inode->i_dev, tmp, blocksize); + if (tmp != *iblock) { + brelse(*bh); + *bh = NULL; + return 1; + } + if (!*bh) + return -1; + return 0; +} + +static int sync_direct(struct inode *inode, int wait) +{ + int i; + int rc, err = 0; + + for (i = 0; i < 7; i++) { + rc = sync_block(inode, + (unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + return err; +} + +static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait) +{ + int i; + struct buffer_head *ind_bh; + int rc, err = 0; + + rc = sync_iblock(inode, iblock, &ind_bh, wait); + if (rc || !ind_bh) + return rc; + + for (i = 0; i < 512; i++) { + rc = sync_block(inode, + ((unsigned short *) ind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse(ind_bh); + return err; +} + +static int sync_dindirect(struct inode *inode, unsigned short *diblock, + int wait) +{ + int i; + struct buffer_head *dind_bh; + int rc, err = 0; + + rc = sync_iblock(inode, diblock, &dind_bh, wait); + if (rc || !dind_bh) + return rc; + + for (i = 0; i < 512; i++) { + rc = sync_indirect(inode, + ((unsigned short *) dind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse(dind_bh); + return err; +} + +int qnx4_sync_file(struct file *file, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int wait, err = 0; + + (void) file; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; + + for (wait = 0; wait <= 1; wait++) { + err |= sync_direct(inode, wait); + } + err |= qnx4_sync_inode(inode); + return (err < 0) ? -EIO : 0; +} + +#endif diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c new file mode 100644 index 000000000..626167044 --- /dev/null +++ b/fs/qnx4/inode.c @@ -0,0 +1,464 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 01-06-1998 by Richard Frowijn : first release. + * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc. + * 30-06-1998 by Frank Denis : first step to write inodes. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/qnx4_fs.h> +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/init.h> + +#include <asm/uaccess.h> + +#define QNX4_VERSION 4 +#define QNX4_BMNAME ".bitmap" +#define CHECK_BOOT_SIGNATURE 0 + +static struct super_operations qnx4_sops; + +#ifdef CONFIG_QNX4FS_RW + +int qnx4_sync_inode(struct inode *inode) +{ + int err = 0; +# if 0 + struct buffer_head *bh; + + bh = qnx4_update_inode(inode); + if (bh && buffer_dirty(bh)) + { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + { + printk ("IO error syncing qnx4 inode [%s:%08lx]\n", + kdevname(inode->i_dev), inode->i_ino); + err = -1; + } + brelse (bh); + } else if (!bh) { + err = -1; + } +# endif + + return err; +} + +static void qnx4_delete_inode(struct inode *inode) +{ + QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); + inode->i_size = 0; + qnx4_truncate(inode); + qnx4_free_inode(inode); +} + +static void qnx4_write_super(struct super_block *sb) +{ + QNX4DEBUG(("qnx4: write_super\n")); + sb->s_dirt = 0; +} + +static void qnx4_put_inode(struct inode *inode) +{ + if (inode->i_nlink != 0) { + return; + } + inode->i_size = 0; +} + +static void qnx4_write_inode(struct inode *inode) +{ + struct qnx4_inode_entry *raw_inode; + int block, ino; + struct buffer_head *bh; + ino = inode->i_ino; + + QNX4DEBUG(("qnx4: write inode 1.\n")); + if (inode->i_nlink == 0) { + return; + } + if (!ino) { + printk("qnx4: bad inode number on dev %s: %d is out of range\n", + kdevname(inode->i_dev), ino); + return; + } + QNX4DEBUG(("qnx4: write inode 2.\n")); + block = ino / QNX4_INODES_PER_BLOCK; + if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) { + printk("qnx4: major problem: unable to read inode from dev " + "%s\n", kdevname(inode->i_dev)); + return; + } + raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + + (ino % QNX4_INODES_PER_BLOCK); + raw_inode->di_mode = inode->i_mode; + raw_inode->di_uid = inode->i_uid; + raw_inode->di_gid = inode->i_gid; + raw_inode->di_nlink = inode->i_nlink; + raw_inode->di_size = inode->i_size; + raw_inode->di_mtime = inode->i_mtime; + raw_inode->di_atime = inode->i_atime; + raw_inode->di_ctime = inode->i_ctime; + raw_inode->di_first_xtnt.xtnt_size = inode->i_blocks; + mark_buffer_dirty(bh, 1); + brelse(bh); +} + +#endif + +static struct super_block *qnx4_read_super(struct super_block *, void *, int); +static void qnx4_put_super(struct super_block *sb); +static void qnx4_read_inode(struct inode *); +static int qnx4_remount(struct super_block *sb, int *flags, char *data); +static int qnx4_statfs(struct super_block *, struct statfs *, int); + +static struct super_operations qnx4_sops = +{ + qnx4_read_inode, +#ifdef CONFIG_QNX4FS_RW + qnx4_write_inode, +#else + NULL, +#endif +#ifdef CONFIG_QNX4FS_RW + qnx4_put_inode, + qnx4_delete_inode, + NULL, /* notify_change */ +#else + NULL, /* put_inode */ + NULL, /* delete_inode */ + NULL, /* notify_change */ +#endif + qnx4_put_super, +#ifdef CONFIG_QNX4FS_RW + qnx4_write_super, +#else + NULL, +#endif + qnx4_statfs, + qnx4_remount, + NULL /* clear_inode */ +}; + +static int qnx4_remount(struct super_block *sb, int *flags, char *data) +{ + struct qnx4_sb_info *qs; + + qs = &sb->u.qnx4_sb; + qs->Version = QNX4_VERSION; + if (*flags & MS_RDONLY) { + return 0; + } + mark_buffer_dirty(qs->sb_buf, 1); + + return 0; +} + +struct buffer_head *inode_getblk(struct inode *inode, int nr, + int create) +{ + int tmp; + int tst; + struct buffer_head *result = NULL; + + tst = nr; + repeat: + tmp = tst; + if (tmp) { + result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE); + if (tmp == tst) { + return result; + } + brelse(result); + goto repeat; + } + if (!create) { + return NULL; + } +#if 0 + tmp = qnx4_new_block(inode->i_sb); + if (!tmp) { + return NULL; + } + result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE); + if (tst) { + qnx4_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + tst = tmp; +#endif + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return result; +} + +struct buffer_head *qnx4_bread(struct inode *inode, int block, int create) +{ + struct buffer_head *bh; + + bh = inode_getblk(inode, block, create); + if (!bh || buffer_uptodate(bh)) { + return bh; + } + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (buffer_uptodate(bh)) { + return bh; + } + brelse(bh); + + return NULL; +} + +static int qnx4_statfs(struct super_block *sb, + struct statfs *buf, int bufsize) +{ + struct statfs tmp; + + memset(&tmp, 0, sizeof tmp); + tmp.f_type = sb->s_magic; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8; + tmp.f_bfree = qnx4_count_free_blocks(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = 0x00; /* change this !!! */ + tmp.f_ffree = qnx4_count_free_inodes(sb); + tmp.f_namelen = QNX4_NAME_MAX; + + return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0; +} + +/* + * Check the root directory of the filesystem to make sure + * it really _is_ a qnx4 filesystem, and to check the size + * of the directory entry. + */ +static const char *qnx4_checkroot(struct super_block *s) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *rootdir; + int rd, rl; + int i, j; + int found = 0; + + if (s == NULL) { + return "no qnx4 filesystem (null superblock)."; + } + if (*(s->u.qnx4_sb.sb->RootDir.di_fname) != '/') { + return "no qnx4 filesystem (no root dir)."; + } else { + QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(s->s_dev))); + rd = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk - 1; + rl = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size; + for (j = 0; j < rl; j++) { + bh = bread(s->s_dev, rd + j, QNX4_BLOCK_SIZE); /* root dir, first block */ + if (bh == NULL) { + return "unable to read root entry."; + } + for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { + rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); + if (rootdir->di_fname != NULL) { + QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname)); + if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) { + found = 1; + s->u.qnx4_sb.BitMap = rootdir; /* keep bitmap inode known */ + break; + } + } + } + brelse(bh); + if (found != 0) { + break; + } + } + if (found == 0) { + return "bitmap file not found."; + } + } + return NULL; +} + +static struct super_block *qnx4_read_super(struct super_block *s, + void *data, int silent) +{ + struct buffer_head *bh; + kdev_t dev = s->s_dev; +#if CHECK_BOOT_SIGNATURE + char *tmpc; +#endif + const char *errmsg; + + MOD_INC_USE_COUNT; + lock_super(s); + set_blocksize(dev, QNX4_BLOCK_SIZE); + s->s_blocksize = QNX4_BLOCK_SIZE; + s->s_blocksize_bits = 9; + s->s_dev = dev; + +#if CHECK_BOOT_SIGNATURE + bh = bread(dev, 0, QNX4_BLOCK_SIZE); + if (!bh) { + printk("qnx4: unable to read the boot sector\n"); + goto outnobh; + } + tmpc = (char *) bh->b_data; + if (tmpc[4] != 'Q' || tmpc[5] != 'N' || tmpc[6] != 'X' || + tmpc[7] != '4' || tmpc[8] != 'F' || tmpc[9] != 'S') { + printk("qnx4: wrong fsid in boot sector.\n"); + goto out; + } + brelse(bh); +#endif + bh = bread(dev, 1, QNX4_BLOCK_SIZE); + if (!bh) { + printk("qnx4: unable to read the superblock\n"); + goto outnobh; + } + s->s_op = &qnx4_sops; + s->s_magic = QNX4_SUPER_MAGIC; +#ifndef CONFIG_QNX4FS_RW + s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ +#endif + s->u.qnx4_sb.sb_buf = bh; + s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data; + s->s_root = + d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK), NULL); + if (s->s_root == NULL) { + printk("qnx4: get inode failed\n"); + goto out; + } + errmsg = qnx4_checkroot(s); + if (errmsg != NULL) { + printk("qnx4: %s\n", errmsg); + goto out; + } + brelse(bh); + unlock_super(s); + s->s_dirt = 1; + + return s; + + out: + brelse(bh); + outnobh: + s->s_dev = 0; + unlock_super(s); + MOD_DEC_USE_COUNT; + + return NULL; +} + +static void qnx4_put_super(struct super_block *sb) +{ + MOD_DEC_USE_COUNT; + return; +} + +static void qnx4_read_inode(struct inode *inode) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *raw_inode; + int block, ino; + + ino = inode->i_ino; + inode->i_op = NULL; + inode->i_mode = 0; + + QNX4DEBUG(("Reading inode : [%d]\n", ino)); + if (!ino) { + printk("qnx4: bad inode number on dev %s: %d is out of range\n", + kdevname(inode->i_dev), ino); + return; + } + block = ino / QNX4_INODES_PER_BLOCK; + + if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) { + printk("qnx4: major problem: unable to read inode from dev " + "%s\n", kdevname(inode->i_dev)); + return; + } + raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + + (ino % QNX4_INODES_PER_BLOCK); + + inode->i_mode = raw_inode->di_mode; + inode->i_uid = raw_inode->di_uid; + inode->i_gid = raw_inode->di_gid; + inode->i_nlink = raw_inode->di_nlink; + inode->i_size = raw_inode->di_size; + inode->i_mtime = raw_inode->di_mtime; + inode->i_atime = raw_inode->di_atime; + inode->i_ctime = raw_inode->di_ctime; + inode->i_blocks = raw_inode->di_first_xtnt.xtnt_size; + 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)) { + inode->i_op = &qnx4_file_inode_operations; + } 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 { + if (S_ISCHR(inode->i_mode)) { + inode->i_op = &chrdev_inode_operations; + } else { + if (S_ISBLK(inode->i_mode)) { + inode->i_op = &blkdev_inode_operations; + } else { + if (S_ISFIFO(inode->i_mode)) { + init_fifo(inode); + } + } + } + } + } + } + brelse(bh); +} + +static struct file_system_type qnx4_fs_type = +{ + "qnx4", + FS_REQUIRES_DEV, + qnx4_read_super, + NULL +}; + +__initfunc(int init_qnx4_fs(void)) +{ + printk("QNX4 filesystem v0.2 registered.\n"); + return register_filesystem(&qnx4_fs_type); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + return init_qnx4_fs(); +} + +void cleanup_module(void) +{ + unregister_filesystem(&qnx4_fs_type); +} + +#endif diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c new file mode 100644 index 000000000..99cbf05eb --- /dev/null +++ b/fs/qnx4/namei.c @@ -0,0 +1,279 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 01-06-1998 by Richard Frowijn : first release. + * 21-06-1998 by Frank Denis : dcache support, fixed error codes. + * 04-07-1998 by Frank Denis : first step for rmdir/unlink. + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/qnx4_fs.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/errno.h> + +#include <asm/segment.h> + +/* + * check if the filename is correct. For some obscure reason, qnx writes a + * new file twice in the directory entry, first with all possible options at 0 + * and for a second time the way it is, they want us not to access the qnx + * filesystem when whe are using linux. + */ +static int qnx4_match(int len, const char *name, + struct buffer_head *bh, unsigned long *offset) +{ + struct qnx4_inode_entry *de; + int namelen; + + if (bh == NULL) { + printk("qnx4: matching unassigned buffer !\n"); + return 0; + } + de = (struct qnx4_inode_entry *) (bh->b_data + *offset); + *offset += QNX4_DIR_ENTRY_SIZE; + if ((de->di_status & 0x08) == 0x08) { + namelen = QNX4_NAME_MAX; + } else { + namelen = _SHORT_NAME_MAX; + } + /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ + if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) { + return 1; + } + if (len != strlen(de->di_fname)) { + return 0; + } + if (strncmp(name, de->di_fname, len) == 0) { + if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) { + if (de->di_status) { + return 1; + } + } + } + return 0; +} + +static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, + const char *name, struct qnx4_inode_entry **res_dir, int *ino) +{ + unsigned long block, offset, blkofs; + struct buffer_head *bh; + + *res_dir = NULL; + if (!dir || !dir->i_sb) { + if (!dir) { + printk("qnx4: NULL dir.\n"); + } else { + printk("qnx4: no superblock on dir.\n"); + } + return NULL; + } + bh = NULL; + blkofs = dir->u.qnx4_i.i_first_xtnt.xtnt_blk - 1; + offset = block = 0; + while (block * QNX4_BLOCK_SIZE + offset < dir->i_size) { + if (!bh) { + bh = qnx4_bread(dir, block + blkofs, 0); + if (!bh) { + block++; + continue; + } + } + *res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset); + if (qnx4_match(len, name, bh, &offset)) { + *ino = (block + blkofs) * QNX4_INODES_PER_BLOCK + + (offset / QNX4_DIR_ENTRY_SIZE) - 1; + return bh; + } + if (offset < bh->b_size) { + continue; + } + brelse(bh); + bh = NULL; + offset = 0; + block++; + } + brelse(bh); + *res_dir = NULL; + return NULL; +} + +int qnx4_lookup(struct inode *dir, struct dentry *dentry) +{ + int ino; + struct qnx4_inode_entry *de; + struct qnx4_link_info *lnk; + struct buffer_head *bh; + const char *name = dentry->d_name.name; + int len = dentry->d_name.len; + struct inode *foundinode; + + if (!dir) { + return -EBADF; + } + if (!S_ISDIR(dir->i_mode)) { + return -EBADF; + } + if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) { + return -ENOENT; + } + /* The entry is linked, let's get the real info */ + if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { + lnk = (struct qnx4_link_info *) de; + ino = (lnk->dl_inode_blk - 1) * QNX4_INODES_PER_BLOCK + + lnk->dl_inode_ndx; + } + brelse(bh); + + if ((foundinode = iget(dir->i_sb, ino)) == NULL) { + QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); + return -EACCES; + } + d_add(dentry, foundinode); + + return 0; +} + +#ifdef CONFIG_QNX4FS_RW +int qnx4_create(struct inode *dir, struct dentry *dentry, int mode) +{ + QNX4DEBUG(("qnx4: qnx4_create\n")); + if (dir == NULL) { + return -ENOENT; + } + return -ENOSPC; +} + +int qnx4_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *de; + struct inode *inode; + int retval; + int ino; + + QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); + bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, + &de, &ino); + if (bh == NULL) { + return -ENOENT; + } + if ((inode = iget(dir->i_sb, ino)) == NULL) { + QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); + retval = -EACCES; + goto end_rmdir; + } + retval = -EPERM; + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) { + QNX4DEBUG(("qnx4: rmdir->capabilities\n")); + goto end_rmdir; + } + if (inode->i_dev != dir->i_dev) { + QNX4DEBUG(("qnx4: rmdir->different devices\n")); + goto end_rmdir; + } + if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ + QNX4DEBUG(("qnx4: inode==dir\n")); + goto end_rmdir; + } + if (!S_ISDIR(inode->i_mode)) { + QNX4DEBUG(("qnx4: rmdir->not a directory\n")); + retval = -ENOTDIR; + goto end_rmdir; + } +#if 0 + if (!empty_dir(inode)) { + retval = -ENOTEMPTY; + goto end_rmdir; + } +#endif + if (dentry->d_count > 1) { + retval = -EBUSY; + goto end_rmdir; + } + if (inode->i_nlink != 2) { + QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink)); + } + QNX4DEBUG(("qnx4: deleting directory\n")); + de->di_status = 0; + memset(de->di_fname, 0, sizeof de->di_fname); + de->di_mode = 0; + mark_buffer_dirty(bh, 1); + inode->i_nlink = 0; + mark_inode_dirty(inode); + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_nlink--; + mark_inode_dirty(dir); + d_delete(dentry); + retval = 0; + + end_rmdir: + brelse(bh); + + return retval; +} + +int qnx4_unlink(struct inode *dir, struct dentry *dentry) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *de; + struct inode *inode; + int retval; + int ino; + + QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); + bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, + &de, &ino); + if (bh == NULL) { + return -ENOENT; + } + if ((inode = iget(dir->i_sb, ino)) == NULL) { + QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); + retval = -EACCES; + goto end_unlink; + } + retval = -EPERM; + if (S_ISDIR(inode->i_mode)) { + goto end_unlink; + } + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) { + goto end_unlink; + } + if (!inode->i_nlink) { + QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n", + kdevname(inode->i_dev), + inode->i_ino, inode->i_nlink)); + inode->i_nlink = 1; + } + de->di_status = 0; + memset(de->di_fname, 0, sizeof de->di_fname); + de->di_mode = 0; + mark_buffer_dirty(bh, 1); + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(dir); + inode->i_nlink--; + inode->i_ctime = dir->i_ctime; + mark_inode_dirty(inode); + d_delete(dentry); + retval = 0; + + end_unlink: + brelse(bh); + + return retval; +} +#endif diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c new file mode 100644 index 000000000..f590d04a6 --- /dev/null +++ b/fs/qnx4/symlinks.c @@ -0,0 +1,121 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.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+ + */ + +/* THIS FILE HAS TO BE REWRITTEN */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/qnx4_fs.h> +#include <linux/stat.h> + +#include <asm/segment.h> +#include <asm/uaccess.h> + +static int qnx4_readlink(struct dentry *, char *, int); +static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *); + +/* + * symlinks can't do much... + */ +struct inode_operations qnx4_symlink_inode_operations = +{ + NULL, /* no file-operations */ +#ifdef CONFIG_QNX4FS_RW + qnx4_create, /* create */ +#else + NULL, +#endif + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + qnx4_readlink, /* readlink */ + qnx4_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +static struct dentry *qnx4_follow_link(struct dentry *dentry, + struct dentry *base) +{ +#if 0 + struct inode *inode = dentry->d_inode; + struct buffer_head *bh; + + if (!inode) { + return ERR_PTR(-ENOENT); + } + if (current->link_count > 5) { + return ERR_PTR(-ELOOP); + } + if (!(bh = qnx4_bread(inode, 0, 0))) { + return ERR_PTR(-EIO); + } + current->link_count++; + current->link_count--; + brelse(bh); + return 0; +#else + printk("qnx4: qnx4_follow_link needs to be fixed.\n"); + return ERR_PTR(-EIO); +#endif +} + +static int qnx4_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct buffer_head *bh; + int i; + char c; + struct qnx4_inode_info *qnx4_ino; + + QNX4DEBUG(("qnx4: qnx4_readlink() called\n")); + + if (buffer == NULL || inode == NULL || !S_ISLNK(inode->i_mode)) { + return -EINVAL; + } + qnx4_ino = &inode->u.qnx4_i; + if (buflen > 1023) { + buflen = 1023; + } + bh = bread(inode->i_dev, qnx4_ino->i_first_xtnt.xtnt_blk, + QNX4_BLOCK_SIZE); + QNX4DEBUG(("qnx4: qnx4_bread sym called -> [%s]\n", + bh->b_data)); + if (bh == NULL) { + QNX4DEBUG(("qnx4: NULL symlink bh\n")); + return 0; + } + if (bh->b_data[0] != 0) { + i = 0; + while (i < buflen && (c = bh->b_data[i])) { + i++; + put_user(c, buffer++); + } + brelse(bh); + return i; + } else { + brelse(bh); + memcpy(buffer, "fixme", 5); + return 5; + } +} diff --git a/fs/qnx4/truncate.c b/fs/qnx4/truncate.c new file mode 100644 index 000000000..a25bf1daf --- /dev/null +++ b/fs/qnx4/truncate.c @@ -0,0 +1,38 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 30-06-1998 by Frank DENIS : ugly filler. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/qnx4_fs.h> +#include <linux/fs.h> +#include <linux/locks.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_QNX4FS_RW + +void qnx4_truncate(struct inode *inode) +{ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) { + return; + } + if (!(S_ISDIR(inode->i_mode))) { + /* TODO */ + } + QNX4DEBUG(("qnx4: qnx4_truncate called\n")); + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} + +#endif |