diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/qnx4 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/qnx4')
-rw-r--r-- | fs/qnx4/Makefile | 7 | ||||
-rw-r--r-- | fs/qnx4/README | 9 | ||||
-rw-r--r-- | fs/qnx4/bitmap.c | 165 | ||||
-rw-r--r-- | fs/qnx4/dir.c | 99 | ||||
-rw-r--r-- | fs/qnx4/file.c | 42 | ||||
-rw-r--r-- | fs/qnx4/fsync.c | 170 | ||||
-rw-r--r-- | fs/qnx4/inode.c | 603 | ||||
-rw-r--r-- | fs/qnx4/namei.c | 249 | ||||
-rw-r--r-- | fs/qnx4/truncate.c | 39 |
9 files changed, 1383 insertions, 0 deletions
diff --git a/fs/qnx4/Makefile b/fs/qnx4/Makefile new file mode 100644 index 000000000000..502d7fe98bab --- /dev/null +++ b/fs/qnx4/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for the linux qnx4-filesystem routines. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_QNX4FS_FS) += qnx4.o | ||
6 | |||
7 | qnx4-objs := inode.o dir.o namei.o file.o bitmap.o truncate.o fsync.o | ||
diff --git a/fs/qnx4/README b/fs/qnx4/README new file mode 100644 index 000000000000..1f1e320d91da --- /dev/null +++ b/fs/qnx4/README | |||
@@ -0,0 +1,9 @@ | |||
1 | |||
2 | This is a snapshot of the QNX4 filesystem for Linux. | ||
3 | Please send diffs and remarks to <al@alarsen.net> . | ||
4 | |||
5 | Credits : | ||
6 | |||
7 | Richard "Scuba" A. Frowijn <scuba@wxs.nl> | ||
8 | Frank "Jedi/Sector One" Denis <j@pureftpd.org> | ||
9 | Anders Larsen <al@alarsen.net> (Maintainer) | ||
diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c new file mode 100644 index 000000000000..991253927658 --- /dev/null +++ b/fs/qnx4/bitmap.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.2.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 28-05-1998 by Richard Frowijn : first release. | ||
11 | * 20-06-1998 by Frank Denis : basic optimisations. | ||
12 | * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap . | ||
13 | * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) . | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/time.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/qnx4_fs.h> | ||
20 | #include <linux/stat.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/buffer_head.h> | ||
24 | #include <linux/bitops.h> | ||
25 | |||
26 | int qnx4_new_block(struct super_block *sb) | ||
27 | { | ||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | static void count_bits(register const char *bmPart, register int size, | ||
32 | int *const tf) | ||
33 | { | ||
34 | char b; | ||
35 | int tot = *tf; | ||
36 | |||
37 | if (size > QNX4_BLOCK_SIZE) { | ||
38 | size = QNX4_BLOCK_SIZE; | ||
39 | } | ||
40 | do { | ||
41 | b = *bmPart++; | ||
42 | if ((b & 1) == 0) | ||
43 | tot++; | ||
44 | if ((b & 2) == 0) | ||
45 | tot++; | ||
46 | if ((b & 4) == 0) | ||
47 | tot++; | ||
48 | if ((b & 8) == 0) | ||
49 | tot++; | ||
50 | if ((b & 16) == 0) | ||
51 | tot++; | ||
52 | if ((b & 32) == 0) | ||
53 | tot++; | ||
54 | if ((b & 64) == 0) | ||
55 | tot++; | ||
56 | if ((b & 128) == 0) | ||
57 | tot++; | ||
58 | size--; | ||
59 | } while (size != 0); | ||
60 | *tf = tot; | ||
61 | } | ||
62 | |||
63 | unsigned long qnx4_count_free_blocks(struct super_block *sb) | ||
64 | { | ||
65 | int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; | ||
66 | int total = 0; | ||
67 | int total_free = 0; | ||
68 | int offset = 0; | ||
69 | int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); | ||
70 | struct buffer_head *bh; | ||
71 | |||
72 | while (total < size) { | ||
73 | if ((bh = sb_bread(sb, start + offset)) == NULL) { | ||
74 | printk("qnx4: I/O error in counting free blocks\n"); | ||
75 | break; | ||
76 | } | ||
77 | count_bits(bh->b_data, size - total, &total_free); | ||
78 | brelse(bh); | ||
79 | total += QNX4_BLOCK_SIZE; | ||
80 | offset++; | ||
81 | } | ||
82 | |||
83 | return total_free; | ||
84 | } | ||
85 | |||
86 | #ifdef CONFIG_QNX4FS_RW | ||
87 | |||
88 | int qnx4_is_free(struct super_block *sb, long block) | ||
89 | { | ||
90 | int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; | ||
91 | int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); | ||
92 | struct buffer_head *bh; | ||
93 | const char *g; | ||
94 | int ret = -EIO; | ||
95 | |||
96 | start += block / (QNX4_BLOCK_SIZE * 8); | ||
97 | QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n", | ||
98 | (unsigned long) block, (unsigned long) start)); | ||
99 | (void) size; /* CHECKME */ | ||
100 | bh = sb_bread(sb, start); | ||
101 | if (bh == NULL) { | ||
102 | return -EIO; | ||
103 | } | ||
104 | g = bh->b_data + (block % QNX4_BLOCK_SIZE); | ||
105 | if (((*g) & (1 << (block % 8))) == 0) { | ||
106 | QNX4DEBUG(("qnx4: is_free -> block is free\n")); | ||
107 | ret = 1; | ||
108 | } else { | ||
109 | QNX4DEBUG(("qnx4: is_free -> block is busy\n")); | ||
110 | ret = 0; | ||
111 | } | ||
112 | brelse(bh); | ||
113 | |||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | int qnx4_set_bitmap(struct super_block *sb, long block, int busy) | ||
118 | { | ||
119 | int start = le32_to_cpu(qnx4_sb(sb)->BitMap->di_first_xtnt.xtnt_blk) - 1; | ||
120 | int size = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size); | ||
121 | struct buffer_head *bh; | ||
122 | char *g; | ||
123 | |||
124 | start += block / (QNX4_BLOCK_SIZE * 8); | ||
125 | QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n", | ||
126 | (unsigned long) block, (unsigned long) start)); | ||
127 | (void) size; /* CHECKME */ | ||
128 | bh = sb_bread(sb, start); | ||
129 | if (bh == NULL) { | ||
130 | return -EIO; | ||
131 | } | ||
132 | g = bh->b_data + (block % QNX4_BLOCK_SIZE); | ||
133 | if (busy == 0) { | ||
134 | (*g) &= ~(1 << (block % 8)); | ||
135 | } else { | ||
136 | (*g) |= (1 << (block % 8)); | ||
137 | } | ||
138 | mark_buffer_dirty(bh); | ||
139 | brelse(bh); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static void qnx4_clear_inode(struct inode *inode) | ||
145 | { | ||
146 | struct qnx4_inode_entry *qnx4_ino = qnx4_raw_inode(inode); | ||
147 | /* What for? */ | ||
148 | memset(qnx4_ino->di_fname, 0, sizeof qnx4_ino->di_fname); | ||
149 | qnx4_ino->di_size = 0; | ||
150 | qnx4_ino->di_num_xtnts = 0; | ||
151 | qnx4_ino->di_mode = 0; | ||
152 | qnx4_ino->di_status = 0; | ||
153 | } | ||
154 | |||
155 | void qnx4_free_inode(struct inode *inode) | ||
156 | { | ||
157 | if (inode->i_ino < 1) { | ||
158 | printk("free_inode: inode 0 or nonexistent inode\n"); | ||
159 | return; | ||
160 | } | ||
161 | qnx4_clear_inode(inode); | ||
162 | clear_inode(inode); | ||
163 | } | ||
164 | |||
165 | #endif | ||
diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c new file mode 100644 index 000000000000..cd66147cca04 --- /dev/null +++ b/fs/qnx4/dir.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.2.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 28-05-1998 by Richard Frowijn : first release. | ||
11 | * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/qnx4_fs.h> | ||
19 | #include <linux/stat.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <linux/buffer_head.h> | ||
22 | |||
23 | |||
24 | static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
25 | { | ||
26 | struct inode *inode = filp->f_dentry->d_inode; | ||
27 | unsigned int offset; | ||
28 | struct buffer_head *bh; | ||
29 | struct qnx4_inode_entry *de; | ||
30 | struct qnx4_link_info *le; | ||
31 | unsigned long blknum; | ||
32 | int ix, ino; | ||
33 | int size; | ||
34 | |||
35 | QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); | ||
36 | QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos)); | ||
37 | |||
38 | lock_kernel(); | ||
39 | |||
40 | while (filp->f_pos < inode->i_size) { | ||
41 | blknum = qnx4_block_map( inode, filp->f_pos >> QNX4_BLOCK_SIZE_BITS ); | ||
42 | bh = sb_bread(inode->i_sb, blknum); | ||
43 | if(bh==NULL) { | ||
44 | printk(KERN_ERR "qnx4_readdir: bread failed (%ld)\n", blknum); | ||
45 | break; | ||
46 | } | ||
47 | ix = (int)(filp->f_pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK; | ||
48 | while (ix < QNX4_INODES_PER_BLOCK) { | ||
49 | offset = ix * QNX4_DIR_ENTRY_SIZE; | ||
50 | de = (struct qnx4_inode_entry *) (bh->b_data + offset); | ||
51 | size = strlen(de->di_fname); | ||
52 | if (size) { | ||
53 | if ( !( de->di_status & QNX4_FILE_LINK ) && size > QNX4_SHORT_NAME_MAX ) | ||
54 | size = QNX4_SHORT_NAME_MAX; | ||
55 | else if ( size > QNX4_NAME_MAX ) | ||
56 | size = QNX4_NAME_MAX; | ||
57 | |||
58 | if ( ( de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK) ) != 0 ) { | ||
59 | QNX4DEBUG(("qnx4_readdir:%.*s\n", size, de->di_fname)); | ||
60 | if ( ( de->di_status & QNX4_FILE_LINK ) == 0 ) | ||
61 | ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1; | ||
62 | else { | ||
63 | le = (struct qnx4_link_info*)de; | ||
64 | ino = ( le->dl_inode_blk - 1 ) * | ||
65 | QNX4_INODES_PER_BLOCK + | ||
66 | le->dl_inode_ndx; | ||
67 | } | ||
68 | if (filldir(dirent, de->di_fname, size, filp->f_pos, ino, DT_UNKNOWN) < 0) { | ||
69 | brelse(bh); | ||
70 | goto out; | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | ix++; | ||
75 | filp->f_pos += QNX4_DIR_ENTRY_SIZE; | ||
76 | } | ||
77 | brelse(bh); | ||
78 | } | ||
79 | out: | ||
80 | unlock_kernel(); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | struct file_operations qnx4_dir_operations = | ||
85 | { | ||
86 | .read = generic_read_dir, | ||
87 | .readdir = qnx4_readdir, | ||
88 | .fsync = file_fsync, | ||
89 | }; | ||
90 | |||
91 | struct inode_operations qnx4_dir_inode_operations = | ||
92 | { | ||
93 | .lookup = qnx4_lookup, | ||
94 | #ifdef CONFIG_QNX4FS_RW | ||
95 | .create = qnx4_create, | ||
96 | .unlink = qnx4_unlink, | ||
97 | .rmdir = qnx4_rmdir, | ||
98 | #endif | ||
99 | }; | ||
diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c new file mode 100644 index 000000000000..b471315e24ef --- /dev/null +++ b/fs/qnx4/file.c | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.2.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 25-05-1998 by Richard Frowijn : first release. | ||
11 | * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read. | ||
12 | * 27-06-1998 by Frank Denis : file overwriting. | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/qnx4_fs.h> | ||
20 | |||
21 | /* | ||
22 | * We have mostly NULL's here: the current defaults are ok for | ||
23 | * the qnx4 filesystem. | ||
24 | */ | ||
25 | struct file_operations qnx4_file_operations = | ||
26 | { | ||
27 | .llseek = generic_file_llseek, | ||
28 | .read = generic_file_read, | ||
29 | .mmap = generic_file_mmap, | ||
30 | .sendfile = generic_file_sendfile, | ||
31 | #ifdef CONFIG_QNX4FS_RW | ||
32 | .write = generic_file_write, | ||
33 | .fsync = qnx4_sync_file, | ||
34 | #endif | ||
35 | }; | ||
36 | |||
37 | struct inode_operations qnx4_file_inode_operations = | ||
38 | { | ||
39 | #ifdef CONFIG_QNX4FS_RW | ||
40 | .truncate = qnx4_truncate, | ||
41 | #endif | ||
42 | }; | ||
diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c new file mode 100644 index 000000000000..df5bc75d5414 --- /dev/null +++ b/fs/qnx4/fsync.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 24-03-1998 by Richard Frowijn : first release. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/time.h> | ||
16 | #include <linux/stat.h> | ||
17 | #include <linux/fcntl.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/buffer_head.h> | ||
20 | |||
21 | #include <linux/fs.h> | ||
22 | #include <linux/qnx4_fs.h> | ||
23 | |||
24 | #include <asm/system.h> | ||
25 | |||
26 | /* | ||
27 | * The functions for qnx4 fs file synchronization. | ||
28 | */ | ||
29 | |||
30 | #ifdef CONFIG_QNX4FS_RW | ||
31 | |||
32 | static int sync_block(struct inode *inode, unsigned short *block, int wait) | ||
33 | { | ||
34 | struct buffer_head *bh; | ||
35 | unsigned short tmp; | ||
36 | |||
37 | if (!*block) | ||
38 | return 0; | ||
39 | tmp = *block; | ||
40 | bh = sb_find_get_block(inode->i_sb, *block); | ||
41 | if (!bh) | ||
42 | return 0; | ||
43 | if (*block != tmp) { | ||
44 | brelse(bh); | ||
45 | return 1; | ||
46 | } | ||
47 | if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { | ||
48 | brelse(bh); | ||
49 | return -1; | ||
50 | } | ||
51 | if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { | ||
52 | brelse(bh); | ||
53 | return 0; | ||
54 | } | ||
55 | ll_rw_block(WRITE, 1, &bh); | ||
56 | atomic_dec(&bh->b_count); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | #ifdef WTF | ||
61 | static int sync_iblock(struct inode *inode, unsigned short *iblock, | ||
62 | struct buffer_head **bh, int wait) | ||
63 | { | ||
64 | int rc; | ||
65 | unsigned short tmp; | ||
66 | |||
67 | *bh = NULL; | ||
68 | tmp = *iblock; | ||
69 | if (!tmp) | ||
70 | return 0; | ||
71 | rc = sync_block(inode, iblock, wait); | ||
72 | if (rc) | ||
73 | return rc; | ||
74 | *bh = sb_bread(inode->i_sb, tmp); | ||
75 | if (tmp != *iblock) { | ||
76 | brelse(*bh); | ||
77 | *bh = NULL; | ||
78 | return 1; | ||
79 | } | ||
80 | if (!*bh) | ||
81 | return -1; | ||
82 | return 0; | ||
83 | } | ||
84 | #endif | ||
85 | |||
86 | static int sync_direct(struct inode *inode, int wait) | ||
87 | { | ||
88 | int i; | ||
89 | int rc, err = 0; | ||
90 | |||
91 | for (i = 0; i < 7; i++) { | ||
92 | rc = sync_block(inode, | ||
93 | (unsigned short *) qnx4_raw_inode(inode)->di_first_xtnt.xtnt_blk + i, wait); | ||
94 | if (rc > 0) | ||
95 | break; | ||
96 | if (rc) | ||
97 | err = rc; | ||
98 | } | ||
99 | return err; | ||
100 | } | ||
101 | |||
102 | #ifdef WTF | ||
103 | static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait) | ||
104 | { | ||
105 | int i; | ||
106 | struct buffer_head *ind_bh; | ||
107 | int rc, err = 0; | ||
108 | |||
109 | rc = sync_iblock(inode, iblock, &ind_bh, wait); | ||
110 | if (rc || !ind_bh) | ||
111 | return rc; | ||
112 | |||
113 | for (i = 0; i < 512; i++) { | ||
114 | rc = sync_block(inode, | ||
115 | ((unsigned short *) ind_bh->b_data) + i, | ||
116 | wait); | ||
117 | if (rc > 0) | ||
118 | break; | ||
119 | if (rc) | ||
120 | err = rc; | ||
121 | } | ||
122 | brelse(ind_bh); | ||
123 | return err; | ||
124 | } | ||
125 | |||
126 | static int sync_dindirect(struct inode *inode, unsigned short *diblock, | ||
127 | int wait) | ||
128 | { | ||
129 | int i; | ||
130 | struct buffer_head *dind_bh; | ||
131 | int rc, err = 0; | ||
132 | |||
133 | rc = sync_iblock(inode, diblock, &dind_bh, wait); | ||
134 | if (rc || !dind_bh) | ||
135 | return rc; | ||
136 | |||
137 | for (i = 0; i < 512; i++) { | ||
138 | rc = sync_indirect(inode, | ||
139 | ((unsigned short *) dind_bh->b_data) + i, | ||
140 | wait); | ||
141 | if (rc > 0) | ||
142 | break; | ||
143 | if (rc) | ||
144 | err = rc; | ||
145 | } | ||
146 | brelse(dind_bh); | ||
147 | return err; | ||
148 | } | ||
149 | #endif | ||
150 | |||
151 | int qnx4_sync_file(struct file *file, struct dentry *dentry, int unused) | ||
152 | { | ||
153 | struct inode *inode = dentry->d_inode; | ||
154 | int wait, err = 0; | ||
155 | |||
156 | (void) file; | ||
157 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | ||
158 | S_ISLNK(inode->i_mode))) | ||
159 | return -EINVAL; | ||
160 | |||
161 | lock_kernel(); | ||
162 | for (wait = 0; wait <= 1; wait++) { | ||
163 | err |= sync_direct(inode, wait); | ||
164 | } | ||
165 | err |= qnx4_sync_inode(inode); | ||
166 | unlock_kernel(); | ||
167 | return (err < 0) ? -EIO : 0; | ||
168 | } | ||
169 | |||
170 | #endif | ||
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c new file mode 100644 index 000000000000..aa92d6b76a9a --- /dev/null +++ b/fs/qnx4/inode.c | |||
@@ -0,0 +1,603 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.2.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 01-06-1998 by Richard Frowijn : first release. | ||
11 | * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc. | ||
12 | * 30-06-1998 by Frank Denis : first step to write inodes. | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/qnx4_fs.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/highuid.h> | ||
25 | #include <linux/smp_lock.h> | ||
26 | #include <linux/pagemap.h> | ||
27 | #include <linux/buffer_head.h> | ||
28 | #include <linux/vfs.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | |||
31 | #define QNX4_VERSION 4 | ||
32 | #define QNX4_BMNAME ".bitmap" | ||
33 | |||
34 | static struct super_operations qnx4_sops; | ||
35 | |||
36 | #ifdef CONFIG_QNX4FS_RW | ||
37 | |||
38 | int qnx4_sync_inode(struct inode *inode) | ||
39 | { | ||
40 | int err = 0; | ||
41 | # if 0 | ||
42 | struct buffer_head *bh; | ||
43 | |||
44 | bh = qnx4_update_inode(inode); | ||
45 | if (bh && buffer_dirty(bh)) | ||
46 | { | ||
47 | sync_dirty_buffer(bh); | ||
48 | if (buffer_req(bh) && !buffer_uptodate(bh)) | ||
49 | { | ||
50 | printk ("IO error syncing qnx4 inode [%s:%08lx]\n", | ||
51 | inode->i_sb->s_id, inode->i_ino); | ||
52 | err = -1; | ||
53 | } | ||
54 | brelse (bh); | ||
55 | } else if (!bh) { | ||
56 | err = -1; | ||
57 | } | ||
58 | # endif | ||
59 | |||
60 | return err; | ||
61 | } | ||
62 | |||
63 | static void qnx4_delete_inode(struct inode *inode) | ||
64 | { | ||
65 | QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); | ||
66 | inode->i_size = 0; | ||
67 | qnx4_truncate(inode); | ||
68 | lock_kernel(); | ||
69 | qnx4_free_inode(inode); | ||
70 | unlock_kernel(); | ||
71 | } | ||
72 | |||
73 | static void qnx4_write_super(struct super_block *sb) | ||
74 | { | ||
75 | lock_kernel(); | ||
76 | QNX4DEBUG(("qnx4: write_super\n")); | ||
77 | sb->s_dirt = 0; | ||
78 | unlock_kernel(); | ||
79 | } | ||
80 | |||
81 | static int qnx4_write_inode(struct inode *inode, int unused) | ||
82 | { | ||
83 | struct qnx4_inode_entry *raw_inode; | ||
84 | int block, ino; | ||
85 | struct buffer_head *bh; | ||
86 | ino = inode->i_ino; | ||
87 | |||
88 | QNX4DEBUG(("qnx4: write inode 1.\n")); | ||
89 | if (inode->i_nlink == 0) { | ||
90 | return 0; | ||
91 | } | ||
92 | if (!ino) { | ||
93 | printk("qnx4: bad inode number on dev %s: %d is out of range\n", | ||
94 | inode->i_sb->s_id, ino); | ||
95 | return -EIO; | ||
96 | } | ||
97 | QNX4DEBUG(("qnx4: write inode 2.\n")); | ||
98 | block = ino / QNX4_INODES_PER_BLOCK; | ||
99 | lock_kernel(); | ||
100 | if (!(bh = sb_bread(inode->i_sb, block))) { | ||
101 | printk("qnx4: major problem: unable to read inode from dev " | ||
102 | "%s\n", inode->i_sb->s_id); | ||
103 | unlock_kernel(); | ||
104 | return -EIO; | ||
105 | } | ||
106 | raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + | ||
107 | (ino % QNX4_INODES_PER_BLOCK); | ||
108 | raw_inode->di_mode = cpu_to_le16(inode->i_mode); | ||
109 | raw_inode->di_uid = cpu_to_le16(fs_high2lowuid(inode->i_uid)); | ||
110 | raw_inode->di_gid = cpu_to_le16(fs_high2lowgid(inode->i_gid)); | ||
111 | raw_inode->di_nlink = cpu_to_le16(inode->i_nlink); | ||
112 | raw_inode->di_size = cpu_to_le32(inode->i_size); | ||
113 | raw_inode->di_mtime = cpu_to_le32(inode->i_mtime.tv_sec); | ||
114 | raw_inode->di_atime = cpu_to_le32(inode->i_atime.tv_sec); | ||
115 | raw_inode->di_ctime = cpu_to_le32(inode->i_ctime.tv_sec); | ||
116 | raw_inode->di_first_xtnt.xtnt_size = cpu_to_le32(inode->i_blocks); | ||
117 | mark_buffer_dirty(bh); | ||
118 | brelse(bh); | ||
119 | unlock_kernel(); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | #endif | ||
124 | |||
125 | static void qnx4_put_super(struct super_block *sb); | ||
126 | static struct inode *qnx4_alloc_inode(struct super_block *sb); | ||
127 | static void qnx4_destroy_inode(struct inode *inode); | ||
128 | static void qnx4_read_inode(struct inode *); | ||
129 | static int qnx4_remount(struct super_block *sb, int *flags, char *data); | ||
130 | static int qnx4_statfs(struct super_block *, struct kstatfs *); | ||
131 | |||
132 | static struct super_operations qnx4_sops = | ||
133 | { | ||
134 | .alloc_inode = qnx4_alloc_inode, | ||
135 | .destroy_inode = qnx4_destroy_inode, | ||
136 | .read_inode = qnx4_read_inode, | ||
137 | .put_super = qnx4_put_super, | ||
138 | .statfs = qnx4_statfs, | ||
139 | .remount_fs = qnx4_remount, | ||
140 | #ifdef CONFIG_QNX4FS_RW | ||
141 | .write_inode = qnx4_write_inode, | ||
142 | .delete_inode = qnx4_delete_inode, | ||
143 | .write_super = qnx4_write_super, | ||
144 | #endif | ||
145 | }; | ||
146 | |||
147 | static int qnx4_remount(struct super_block *sb, int *flags, char *data) | ||
148 | { | ||
149 | struct qnx4_sb_info *qs; | ||
150 | |||
151 | qs = qnx4_sb(sb); | ||
152 | qs->Version = QNX4_VERSION; | ||
153 | #ifndef CONFIG_QNX4FS_RW | ||
154 | *flags |= MS_RDONLY; | ||
155 | #endif | ||
156 | if (*flags & MS_RDONLY) { | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | mark_buffer_dirty(qs->sb_buf); | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static struct buffer_head *qnx4_getblk(struct inode *inode, int nr, | ||
166 | int create) | ||
167 | { | ||
168 | struct buffer_head *result = NULL; | ||
169 | |||
170 | if ( nr >= 0 ) | ||
171 | nr = qnx4_block_map( inode, nr ); | ||
172 | if (nr) { | ||
173 | result = sb_getblk(inode->i_sb, nr); | ||
174 | return result; | ||
175 | } | ||
176 | if (!create) { | ||
177 | return NULL; | ||
178 | } | ||
179 | #if 0 | ||
180 | tmp = qnx4_new_block(inode->i_sb); | ||
181 | if (!tmp) { | ||
182 | return NULL; | ||
183 | } | ||
184 | result = sb_getblk(inode->i_sb, tmp); | ||
185 | if (tst) { | ||
186 | qnx4_free_block(inode->i_sb, tmp); | ||
187 | brelse(result); | ||
188 | goto repeat; | ||
189 | } | ||
190 | tst = tmp; | ||
191 | #endif | ||
192 | inode->i_ctime = CURRENT_TIME_SEC; | ||
193 | mark_inode_dirty(inode); | ||
194 | return result; | ||
195 | } | ||
196 | |||
197 | struct buffer_head *qnx4_bread(struct inode *inode, int block, int create) | ||
198 | { | ||
199 | struct buffer_head *bh; | ||
200 | |||
201 | bh = qnx4_getblk(inode, block, create); | ||
202 | if (!bh || buffer_uptodate(bh)) { | ||
203 | return bh; | ||
204 | } | ||
205 | ll_rw_block(READ, 1, &bh); | ||
206 | wait_on_buffer(bh); | ||
207 | if (buffer_uptodate(bh)) { | ||
208 | return bh; | ||
209 | } | ||
210 | brelse(bh); | ||
211 | |||
212 | return NULL; | ||
213 | } | ||
214 | |||
215 | static int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_head *bh, int create ) | ||
216 | { | ||
217 | unsigned long phys; | ||
218 | |||
219 | QNX4DEBUG(("qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock)); | ||
220 | |||
221 | phys = qnx4_block_map( inode, iblock ); | ||
222 | if ( phys ) { | ||
223 | // logical block is before EOF | ||
224 | map_bh(bh, inode->i_sb, phys); | ||
225 | } else if ( create ) { | ||
226 | // to be done. | ||
227 | } | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | unsigned long qnx4_block_map( struct inode *inode, long iblock ) | ||
232 | { | ||
233 | int ix; | ||
234 | long offset, i_xblk; | ||
235 | unsigned long block = 0; | ||
236 | struct buffer_head *bh = NULL; | ||
237 | struct qnx4_xblk *xblk = NULL; | ||
238 | struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); | ||
239 | qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); | ||
240 | |||
241 | if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) { | ||
242 | // iblock is in the first extent. This is easy. | ||
243 | block = le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_blk) + iblock - 1; | ||
244 | } else { | ||
245 | // iblock is beyond first extent. We have to follow the extent chain. | ||
246 | i_xblk = le32_to_cpu(qnx4_inode->di_xblk); | ||
247 | offset = iblock - le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size); | ||
248 | ix = 0; | ||
249 | while ( --nxtnt > 0 ) { | ||
250 | if ( ix == 0 ) { | ||
251 | // read next xtnt block. | ||
252 | bh = sb_bread(inode->i_sb, i_xblk - 1); | ||
253 | if ( !bh ) { | ||
254 | QNX4DEBUG(("qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1)); | ||
255 | return -EIO; | ||
256 | } | ||
257 | xblk = (struct qnx4_xblk*)bh->b_data; | ||
258 | if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) { | ||
259 | QNX4DEBUG(("qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk)); | ||
260 | return -EIO; | ||
261 | } | ||
262 | } | ||
263 | if ( offset < le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size) ) { | ||
264 | // got it! | ||
265 | block = le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_blk) + offset - 1; | ||
266 | break; | ||
267 | } | ||
268 | offset -= le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size); | ||
269 | if ( ++ix >= xblk->xblk_num_xtnts ) { | ||
270 | i_xblk = le32_to_cpu(xblk->xblk_next_xblk); | ||
271 | ix = 0; | ||
272 | brelse( bh ); | ||
273 | bh = NULL; | ||
274 | } | ||
275 | } | ||
276 | if ( bh ) | ||
277 | brelse( bh ); | ||
278 | } | ||
279 | |||
280 | QNX4DEBUG(("qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block)); | ||
281 | return block; | ||
282 | } | ||
283 | |||
284 | static int qnx4_statfs(struct super_block *sb, struct kstatfs *buf) | ||
285 | { | ||
286 | lock_kernel(); | ||
287 | |||
288 | buf->f_type = sb->s_magic; | ||
289 | buf->f_bsize = sb->s_blocksize; | ||
290 | buf->f_blocks = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8; | ||
291 | buf->f_bfree = qnx4_count_free_blocks(sb); | ||
292 | buf->f_bavail = buf->f_bfree; | ||
293 | buf->f_namelen = QNX4_NAME_MAX; | ||
294 | |||
295 | unlock_kernel(); | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * Check the root directory of the filesystem to make sure | ||
302 | * it really _is_ a qnx4 filesystem, and to check the size | ||
303 | * of the directory entry. | ||
304 | */ | ||
305 | static const char *qnx4_checkroot(struct super_block *sb) | ||
306 | { | ||
307 | struct buffer_head *bh; | ||
308 | struct qnx4_inode_entry *rootdir; | ||
309 | int rd, rl; | ||
310 | int i, j; | ||
311 | int found = 0; | ||
312 | |||
313 | if (*(qnx4_sb(sb)->sb->RootDir.di_fname) != '/') { | ||
314 | return "no qnx4 filesystem (no root dir)."; | ||
315 | } else { | ||
316 | QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", sb->s_id)); | ||
317 | rd = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_blk) - 1; | ||
318 | rl = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_size); | ||
319 | for (j = 0; j < rl; j++) { | ||
320 | bh = sb_bread(sb, rd + j); /* root dir, first block */ | ||
321 | if (bh == NULL) { | ||
322 | return "unable to read root entry."; | ||
323 | } | ||
324 | for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { | ||
325 | rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); | ||
326 | if (rootdir->di_fname != NULL) { | ||
327 | QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname)); | ||
328 | if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) { | ||
329 | found = 1; | ||
330 | qnx4_sb(sb)->BitMap = kmalloc( sizeof( struct qnx4_inode_entry ), GFP_KERNEL ); | ||
331 | if (!qnx4_sb(sb)->BitMap) { | ||
332 | brelse (bh); | ||
333 | return "not enough memory for bitmap inode"; | ||
334 | } | ||
335 | memcpy( qnx4_sb(sb)->BitMap, rootdir, sizeof( struct qnx4_inode_entry ) ); /* keep bitmap inode known */ | ||
336 | break; | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | brelse(bh); | ||
341 | if (found != 0) { | ||
342 | break; | ||
343 | } | ||
344 | } | ||
345 | if (found == 0) { | ||
346 | return "bitmap file not found."; | ||
347 | } | ||
348 | } | ||
349 | return NULL; | ||
350 | } | ||
351 | |||
352 | static int qnx4_fill_super(struct super_block *s, void *data, int silent) | ||
353 | { | ||
354 | struct buffer_head *bh; | ||
355 | struct inode *root; | ||
356 | const char *errmsg; | ||
357 | struct qnx4_sb_info *qs; | ||
358 | |||
359 | qs = kmalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); | ||
360 | if (!qs) | ||
361 | return -ENOMEM; | ||
362 | s->s_fs_info = qs; | ||
363 | memset(qs, 0, sizeof(struct qnx4_sb_info)); | ||
364 | |||
365 | sb_set_blocksize(s, QNX4_BLOCK_SIZE); | ||
366 | |||
367 | /* Check the superblock signature. Since the qnx4 code is | ||
368 | dangerous, we should leave as quickly as possible | ||
369 | if we don't belong here... */ | ||
370 | bh = sb_bread(s, 1); | ||
371 | if (!bh) { | ||
372 | printk("qnx4: unable to read the superblock\n"); | ||
373 | goto outnobh; | ||
374 | } | ||
375 | if ( le32_to_cpu( *(__u32*)bh->b_data ) != QNX4_SUPER_MAGIC ) { | ||
376 | if (!silent) | ||
377 | printk("qnx4: wrong fsid in superblock.\n"); | ||
378 | goto out; | ||
379 | } | ||
380 | s->s_op = &qnx4_sops; | ||
381 | s->s_magic = QNX4_SUPER_MAGIC; | ||
382 | #ifndef CONFIG_QNX4FS_RW | ||
383 | s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ | ||
384 | #endif | ||
385 | qnx4_sb(s)->sb_buf = bh; | ||
386 | qnx4_sb(s)->sb = (struct qnx4_super_block *) bh->b_data; | ||
387 | |||
388 | |||
389 | /* check before allocating dentries, inodes, .. */ | ||
390 | errmsg = qnx4_checkroot(s); | ||
391 | if (errmsg != NULL) { | ||
392 | if (!silent) | ||
393 | printk("qnx4: %s\n", errmsg); | ||
394 | goto out; | ||
395 | } | ||
396 | |||
397 | /* does root not have inode number QNX4_ROOT_INO ?? */ | ||
398 | root = iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); | ||
399 | if (!root) { | ||
400 | printk("qnx4: get inode failed\n"); | ||
401 | goto out; | ||
402 | } | ||
403 | |||
404 | s->s_root = d_alloc_root(root); | ||
405 | if (s->s_root == NULL) | ||
406 | goto outi; | ||
407 | |||
408 | brelse(bh); | ||
409 | |||
410 | return 0; | ||
411 | |||
412 | outi: | ||
413 | iput(root); | ||
414 | out: | ||
415 | brelse(bh); | ||
416 | outnobh: | ||
417 | kfree(qs); | ||
418 | s->s_fs_info = NULL; | ||
419 | return -EINVAL; | ||
420 | } | ||
421 | |||
422 | static void qnx4_put_super(struct super_block *sb) | ||
423 | { | ||
424 | struct qnx4_sb_info *qs = qnx4_sb(sb); | ||
425 | kfree( qs->BitMap ); | ||
426 | kfree( qs ); | ||
427 | sb->s_fs_info = NULL; | ||
428 | return; | ||
429 | } | ||
430 | |||
431 | static int qnx4_writepage(struct page *page, struct writeback_control *wbc) | ||
432 | { | ||
433 | return block_write_full_page(page,qnx4_get_block, wbc); | ||
434 | } | ||
435 | static int qnx4_readpage(struct file *file, struct page *page) | ||
436 | { | ||
437 | return block_read_full_page(page,qnx4_get_block); | ||
438 | } | ||
439 | static int qnx4_prepare_write(struct file *file, struct page *page, | ||
440 | unsigned from, unsigned to) | ||
441 | { | ||
442 | struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host); | ||
443 | return cont_prepare_write(page, from, to, qnx4_get_block, | ||
444 | &qnx4_inode->mmu_private); | ||
445 | } | ||
446 | static sector_t qnx4_bmap(struct address_space *mapping, sector_t block) | ||
447 | { | ||
448 | return generic_block_bmap(mapping,block,qnx4_get_block); | ||
449 | } | ||
450 | static struct address_space_operations qnx4_aops = { | ||
451 | .readpage = qnx4_readpage, | ||
452 | .writepage = qnx4_writepage, | ||
453 | .sync_page = block_sync_page, | ||
454 | .prepare_write = qnx4_prepare_write, | ||
455 | .commit_write = generic_commit_write, | ||
456 | .bmap = qnx4_bmap | ||
457 | }; | ||
458 | |||
459 | static void qnx4_read_inode(struct inode *inode) | ||
460 | { | ||
461 | struct buffer_head *bh; | ||
462 | struct qnx4_inode_entry *raw_inode; | ||
463 | int block, ino; | ||
464 | struct super_block *sb = inode->i_sb; | ||
465 | struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); | ||
466 | |||
467 | ino = inode->i_ino; | ||
468 | inode->i_mode = 0; | ||
469 | |||
470 | QNX4DEBUG(("Reading inode : [%d]\n", ino)); | ||
471 | if (!ino) { | ||
472 | printk("qnx4: bad inode number on dev %s: %d is out of range\n", | ||
473 | sb->s_id, ino); | ||
474 | return; | ||
475 | } | ||
476 | block = ino / QNX4_INODES_PER_BLOCK; | ||
477 | |||
478 | if (!(bh = sb_bread(sb, block))) { | ||
479 | printk("qnx4: major problem: unable to read inode from dev " | ||
480 | "%s\n", sb->s_id); | ||
481 | return; | ||
482 | } | ||
483 | raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + | ||
484 | (ino % QNX4_INODES_PER_BLOCK); | ||
485 | |||
486 | inode->i_mode = le16_to_cpu(raw_inode->di_mode); | ||
487 | inode->i_uid = (uid_t)le16_to_cpu(raw_inode->di_uid); | ||
488 | inode->i_gid = (gid_t)le16_to_cpu(raw_inode->di_gid); | ||
489 | inode->i_nlink = le16_to_cpu(raw_inode->di_nlink); | ||
490 | inode->i_size = le32_to_cpu(raw_inode->di_size); | ||
491 | inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->di_mtime); | ||
492 | inode->i_mtime.tv_nsec = 0; | ||
493 | inode->i_atime.tv_sec = le32_to_cpu(raw_inode->di_atime); | ||
494 | inode->i_atime.tv_nsec = 0; | ||
495 | inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->di_ctime); | ||
496 | inode->i_ctime.tv_nsec = 0; | ||
497 | inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size); | ||
498 | inode->i_blksize = QNX4_DIR_ENTRY_SIZE; | ||
499 | |||
500 | memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE); | ||
501 | if (S_ISREG(inode->i_mode)) { | ||
502 | inode->i_op = &qnx4_file_inode_operations; | ||
503 | inode->i_fop = &qnx4_file_operations; | ||
504 | inode->i_mapping->a_ops = &qnx4_aops; | ||
505 | qnx4_i(inode)->mmu_private = inode->i_size; | ||
506 | } else if (S_ISDIR(inode->i_mode)) { | ||
507 | inode->i_op = &qnx4_dir_inode_operations; | ||
508 | inode->i_fop = &qnx4_dir_operations; | ||
509 | } else if (S_ISLNK(inode->i_mode)) { | ||
510 | inode->i_op = &page_symlink_inode_operations; | ||
511 | inode->i_mapping->a_ops = &qnx4_aops; | ||
512 | qnx4_i(inode)->mmu_private = inode->i_size; | ||
513 | } else | ||
514 | printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id); | ||
515 | brelse(bh); | ||
516 | } | ||
517 | |||
518 | static kmem_cache_t *qnx4_inode_cachep; | ||
519 | |||
520 | static struct inode *qnx4_alloc_inode(struct super_block *sb) | ||
521 | { | ||
522 | struct qnx4_inode_info *ei; | ||
523 | ei = kmem_cache_alloc(qnx4_inode_cachep, SLAB_KERNEL); | ||
524 | if (!ei) | ||
525 | return NULL; | ||
526 | return &ei->vfs_inode; | ||
527 | } | ||
528 | |||
529 | static void qnx4_destroy_inode(struct inode *inode) | ||
530 | { | ||
531 | kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); | ||
532 | } | ||
533 | |||
534 | static void init_once(void *foo, kmem_cache_t * cachep, | ||
535 | unsigned long flags) | ||
536 | { | ||
537 | struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; | ||
538 | |||
539 | if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == | ||
540 | SLAB_CTOR_CONSTRUCTOR) | ||
541 | inode_init_once(&ei->vfs_inode); | ||
542 | } | ||
543 | |||
544 | static int init_inodecache(void) | ||
545 | { | ||
546 | qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache", | ||
547 | sizeof(struct qnx4_inode_info), | ||
548 | 0, SLAB_RECLAIM_ACCOUNT, | ||
549 | init_once, NULL); | ||
550 | if (qnx4_inode_cachep == NULL) | ||
551 | return -ENOMEM; | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static void destroy_inodecache(void) | ||
556 | { | ||
557 | if (kmem_cache_destroy(qnx4_inode_cachep)) | ||
558 | printk(KERN_INFO | ||
559 | "qnx4_inode_cache: not all structures were freed\n"); | ||
560 | } | ||
561 | |||
562 | static struct super_block *qnx4_get_sb(struct file_system_type *fs_type, | ||
563 | int flags, const char *dev_name, void *data) | ||
564 | { | ||
565 | return get_sb_bdev(fs_type, flags, dev_name, data, qnx4_fill_super); | ||
566 | } | ||
567 | |||
568 | static struct file_system_type qnx4_fs_type = { | ||
569 | .owner = THIS_MODULE, | ||
570 | .name = "qnx4", | ||
571 | .get_sb = qnx4_get_sb, | ||
572 | .kill_sb = kill_block_super, | ||
573 | .fs_flags = FS_REQUIRES_DEV, | ||
574 | }; | ||
575 | |||
576 | static int __init init_qnx4_fs(void) | ||
577 | { | ||
578 | int err; | ||
579 | |||
580 | err = init_inodecache(); | ||
581 | if (err) | ||
582 | return err; | ||
583 | |||
584 | err = register_filesystem(&qnx4_fs_type); | ||
585 | if (err) { | ||
586 | destroy_inodecache(); | ||
587 | return err; | ||
588 | } | ||
589 | |||
590 | printk("QNX4 filesystem 0.2.3 registered.\n"); | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static void __exit exit_qnx4_fs(void) | ||
595 | { | ||
596 | unregister_filesystem(&qnx4_fs_type); | ||
597 | destroy_inodecache(); | ||
598 | } | ||
599 | |||
600 | module_init(init_qnx4_fs) | ||
601 | module_exit(exit_qnx4_fs) | ||
602 | MODULE_LICENSE("GPL"); | ||
603 | |||
diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c new file mode 100644 index 000000000000..4af4951d7f54 --- /dev/null +++ b/fs/qnx4/namei.c | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.2.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 01-06-1998 by Richard Frowijn : first release. | ||
11 | * 21-06-1998 by Frank Denis : dcache support, fixed error codes. | ||
12 | * 04-07-1998 by Frank Denis : first step for rmdir/unlink. | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/time.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/qnx4_fs.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/stat.h> | ||
22 | #include <linux/fcntl.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/buffer_head.h> | ||
26 | |||
27 | |||
28 | /* | ||
29 | * check if the filename is correct. For some obscure reason, qnx writes a | ||
30 | * new file twice in the directory entry, first with all possible options at 0 | ||
31 | * and for a second time the way it is, they want us not to access the qnx | ||
32 | * filesystem when whe are using linux. | ||
33 | */ | ||
34 | static int qnx4_match(int len, const char *name, | ||
35 | struct buffer_head *bh, unsigned long *offset) | ||
36 | { | ||
37 | struct qnx4_inode_entry *de; | ||
38 | int namelen, thislen; | ||
39 | |||
40 | if (bh == NULL) { | ||
41 | printk("qnx4: matching unassigned buffer !\n"); | ||
42 | return 0; | ||
43 | } | ||
44 | de = (struct qnx4_inode_entry *) (bh->b_data + *offset); | ||
45 | *offset += QNX4_DIR_ENTRY_SIZE; | ||
46 | if ((de->di_status & QNX4_FILE_LINK) != 0) { | ||
47 | namelen = QNX4_NAME_MAX; | ||
48 | } else { | ||
49 | namelen = QNX4_SHORT_NAME_MAX; | ||
50 | } | ||
51 | /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ | ||
52 | if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) { | ||
53 | return 1; | ||
54 | } | ||
55 | thislen = strlen( de->di_fname ); | ||
56 | if ( thislen > namelen ) | ||
57 | thislen = namelen; | ||
58 | if (len != thislen) { | ||
59 | return 0; | ||
60 | } | ||
61 | if (strncmp(name, de->di_fname, len) == 0) { | ||
62 | if ((de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK)) != 0) { | ||
63 | return 1; | ||
64 | } | ||
65 | } | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, | ||
70 | const char *name, struct qnx4_inode_entry **res_dir, int *ino) | ||
71 | { | ||
72 | unsigned long block, offset, blkofs; | ||
73 | struct buffer_head *bh; | ||
74 | |||
75 | *res_dir = NULL; | ||
76 | if (!dir->i_sb) { | ||
77 | printk("qnx4: no superblock on dir.\n"); | ||
78 | return NULL; | ||
79 | } | ||
80 | bh = NULL; | ||
81 | block = offset = blkofs = 0; | ||
82 | while (blkofs * QNX4_BLOCK_SIZE + offset < dir->i_size) { | ||
83 | if (!bh) { | ||
84 | bh = qnx4_bread(dir, blkofs, 0); | ||
85 | if (!bh) { | ||
86 | blkofs++; | ||
87 | continue; | ||
88 | } | ||
89 | } | ||
90 | *res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset); | ||
91 | if (qnx4_match(len, name, bh, &offset)) { | ||
92 | block = qnx4_block_map( dir, blkofs ); | ||
93 | *ino = block * QNX4_INODES_PER_BLOCK + | ||
94 | (offset / QNX4_DIR_ENTRY_SIZE) - 1; | ||
95 | return bh; | ||
96 | } | ||
97 | if (offset < bh->b_size) { | ||
98 | continue; | ||
99 | } | ||
100 | brelse(bh); | ||
101 | bh = NULL; | ||
102 | offset = 0; | ||
103 | blkofs++; | ||
104 | } | ||
105 | brelse(bh); | ||
106 | *res_dir = NULL; | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
111 | { | ||
112 | int ino; | ||
113 | struct qnx4_inode_entry *de; | ||
114 | struct qnx4_link_info *lnk; | ||
115 | struct buffer_head *bh; | ||
116 | const char *name = dentry->d_name.name; | ||
117 | int len = dentry->d_name.len; | ||
118 | struct inode *foundinode = NULL; | ||
119 | |||
120 | lock_kernel(); | ||
121 | if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) | ||
122 | goto out; | ||
123 | /* The entry is linked, let's get the real info */ | ||
124 | if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { | ||
125 | lnk = (struct qnx4_link_info *) de; | ||
126 | ino = (le32_to_cpu(lnk->dl_inode_blk) - 1) * | ||
127 | QNX4_INODES_PER_BLOCK + | ||
128 | lnk->dl_inode_ndx; | ||
129 | } | ||
130 | brelse(bh); | ||
131 | |||
132 | if ((foundinode = iget(dir->i_sb, ino)) == NULL) { | ||
133 | unlock_kernel(); | ||
134 | QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); | ||
135 | return ERR_PTR(-EACCES); | ||
136 | } | ||
137 | out: | ||
138 | unlock_kernel(); | ||
139 | d_add(dentry, foundinode); | ||
140 | |||
141 | return NULL; | ||
142 | } | ||
143 | |||
144 | #ifdef CONFIG_QNX4FS_RW | ||
145 | int qnx4_create(struct inode *dir, struct dentry *dentry, int mode, | ||
146 | struct nameidata *nd) | ||
147 | { | ||
148 | QNX4DEBUG(("qnx4: qnx4_create\n")); | ||
149 | if (dir == NULL) { | ||
150 | return -ENOENT; | ||
151 | } | ||
152 | return -ENOSPC; | ||
153 | } | ||
154 | |||
155 | int qnx4_rmdir(struct inode *dir, struct dentry *dentry) | ||
156 | { | ||
157 | struct buffer_head *bh; | ||
158 | struct qnx4_inode_entry *de; | ||
159 | struct inode *inode; | ||
160 | int retval; | ||
161 | int ino; | ||
162 | |||
163 | QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); | ||
164 | lock_kernel(); | ||
165 | bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, | ||
166 | &de, &ino); | ||
167 | if (bh == NULL) { | ||
168 | unlock_kernel(); | ||
169 | return -ENOENT; | ||
170 | } | ||
171 | inode = dentry->d_inode; | ||
172 | if (inode->i_ino != ino) { | ||
173 | retval = -EIO; | ||
174 | goto end_rmdir; | ||
175 | } | ||
176 | #if 0 | ||
177 | if (!empty_dir(inode)) { | ||
178 | retval = -ENOTEMPTY; | ||
179 | goto end_rmdir; | ||
180 | } | ||
181 | #endif | ||
182 | if (inode->i_nlink != 2) { | ||
183 | QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink)); | ||
184 | } | ||
185 | QNX4DEBUG(("qnx4: deleting directory\n")); | ||
186 | de->di_status = 0; | ||
187 | memset(de->di_fname, 0, sizeof de->di_fname); | ||
188 | de->di_mode = 0; | ||
189 | mark_buffer_dirty(bh); | ||
190 | inode->i_nlink = 0; | ||
191 | mark_inode_dirty(inode); | ||
192 | inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; | ||
193 | dir->i_nlink--; | ||
194 | mark_inode_dirty(dir); | ||
195 | retval = 0; | ||
196 | |||
197 | end_rmdir: | ||
198 | brelse(bh); | ||
199 | |||
200 | unlock_kernel(); | ||
201 | return retval; | ||
202 | } | ||
203 | |||
204 | int qnx4_unlink(struct inode *dir, struct dentry *dentry) | ||
205 | { | ||
206 | struct buffer_head *bh; | ||
207 | struct qnx4_inode_entry *de; | ||
208 | struct inode *inode; | ||
209 | int retval; | ||
210 | int ino; | ||
211 | |||
212 | QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); | ||
213 | lock_kernel(); | ||
214 | bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, | ||
215 | &de, &ino); | ||
216 | if (bh == NULL) { | ||
217 | unlock_kernel(); | ||
218 | return -ENOENT; | ||
219 | } | ||
220 | inode = dentry->d_inode; | ||
221 | if (inode->i_ino != ino) { | ||
222 | retval = -EIO; | ||
223 | goto end_unlink; | ||
224 | } | ||
225 | retval = -EPERM; | ||
226 | if (!inode->i_nlink) { | ||
227 | QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n", | ||
228 | inode->i_sb->s_id, | ||
229 | inode->i_ino, inode->i_nlink)); | ||
230 | inode->i_nlink = 1; | ||
231 | } | ||
232 | de->di_status = 0; | ||
233 | memset(de->di_fname, 0, sizeof de->di_fname); | ||
234 | de->di_mode = 0; | ||
235 | mark_buffer_dirty(bh); | ||
236 | dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; | ||
237 | mark_inode_dirty(dir); | ||
238 | inode->i_nlink--; | ||
239 | inode->i_ctime = dir->i_ctime; | ||
240 | mark_inode_dirty(inode); | ||
241 | retval = 0; | ||
242 | |||
243 | end_unlink: | ||
244 | unlock_kernel(); | ||
245 | brelse(bh); | ||
246 | |||
247 | return retval; | ||
248 | } | ||
249 | #endif | ||
diff --git a/fs/qnx4/truncate.c b/fs/qnx4/truncate.c new file mode 100644 index 000000000000..86563ec01b39 --- /dev/null +++ b/fs/qnx4/truncate.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * QNX4 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 0.1 | ||
5 | * | ||
6 | * Using parts of the xiafs filesystem. | ||
7 | * | ||
8 | * History : | ||
9 | * | ||
10 | * 30-06-1998 by Frank DENIS : ugly filler. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/qnx4_fs.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | |||
21 | #ifdef CONFIG_QNX4FS_RW | ||
22 | |||
23 | void qnx4_truncate(struct inode *inode) | ||
24 | { | ||
25 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | ||
26 | S_ISLNK(inode->i_mode))) { | ||
27 | return; | ||
28 | } | ||
29 | lock_kernel(); | ||
30 | if (!(S_ISDIR(inode->i_mode))) { | ||
31 | /* TODO */ | ||
32 | } | ||
33 | QNX4DEBUG(("qnx4: qnx4_truncate called\n")); | ||
34 | inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; | ||
35 | mark_inode_dirty(inode); | ||
36 | unlock_kernel(); | ||
37 | } | ||
38 | |||
39 | #endif | ||