Blame view
fs/qnx4/inode.c
10.9 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* * QNX4 file system, Linux implementation. * * Version : 0.2.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. */ |
1da177e4c
|
14 |
#include <linux/module.h> |
1da177e4c
|
15 |
#include <linux/init.h> |
964f53696
|
16 |
#include <linux/slab.h> |
1da177e4c
|
17 |
#include <linux/highuid.h> |
1da177e4c
|
18 19 |
#include <linux/pagemap.h> #include <linux/buffer_head.h> |
79d257675
|
20 |
#include <linux/writeback.h> |
964f53696
|
21 22 |
#include <linux/statfs.h> #include "qnx4.h" |
1da177e4c
|
23 24 25 |
#define QNX4_VERSION 4 #define QNX4_BMNAME ".bitmap" |
ee9b6d61a
|
26 |
static const struct super_operations qnx4_sops; |
1da177e4c
|
27 |
|
1da177e4c
|
28 29 |
static struct inode *qnx4_alloc_inode(struct super_block *sb); static void qnx4_destroy_inode(struct inode *inode); |
1da177e4c
|
30 |
static int qnx4_remount(struct super_block *sb, int *flags, char *data); |
726c33422
|
31 |
static int qnx4_statfs(struct dentry *, struct kstatfs *); |
1da177e4c
|
32 |
|
ee9b6d61a
|
33 |
static const struct super_operations qnx4_sops = |
1da177e4c
|
34 35 36 |
{ .alloc_inode = qnx4_alloc_inode, .destroy_inode = qnx4_destroy_inode, |
1da177e4c
|
37 38 |
.statfs = qnx4_statfs, .remount_fs = qnx4_remount, |
1da177e4c
|
39 40 41 42 43 |
}; static int qnx4_remount(struct super_block *sb, int *flags, char *data) { struct qnx4_sb_info *qs; |
02b9984d6
|
44 |
sync_filesystem(sb); |
1da177e4c
|
45 46 |
qs = qnx4_sb(sb); qs->Version = QNX4_VERSION; |
1da177e4c
|
47 |
*flags |= MS_RDONLY; |
1da177e4c
|
48 49 |
return 0; } |
1da177e4c
|
50 51 52 |
static int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_head *bh, int create ) { unsigned long phys; |
891ddb95d
|
53 54 |
QNX4DEBUG((KERN_INFO "qnx4: qnx4_get_block inode=[%ld] iblock=[%ld] ",inode->i_ino,iblock)); |
1da177e4c
|
55 56 57 58 59 |
phys = qnx4_block_map( inode, iblock ); if ( phys ) { // logical block is before EOF map_bh(bh, inode->i_sb, phys); |
1da177e4c
|
60 61 62 |
} return 0; } |
7cd916f6e
|
63 64 65 66 67 68 69 70 |
static inline u32 try_extent(qnx4_xtnt_t *extent, u32 *offset) { u32 size = le32_to_cpu(extent->xtnt_size); if (*offset < size) return le32_to_cpu(extent->xtnt_blk) + *offset - 1; *offset -= size; return 0; } |
1da177e4c
|
71 72 73 |
unsigned long qnx4_block_map( struct inode *inode, long iblock ) { int ix; |
7cd916f6e
|
74 |
long i_xblk; |
1da177e4c
|
75 76 77 |
struct buffer_head *bh = NULL; struct qnx4_xblk *xblk = NULL; struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); |
75043cb5b
|
78 |
u16 nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); |
7cd916f6e
|
79 80 |
u32 offset = iblock; u32 block = try_extent(&qnx4_inode->di_first_xtnt, &offset); |
1da177e4c
|
81 |
|
7cd916f6e
|
82 |
if (block) { |
1da177e4c
|
83 |
// iblock is in the first extent. This is easy. |
1da177e4c
|
84 85 86 |
} else { // iblock is beyond first extent. We have to follow the extent chain. i_xblk = le32_to_cpu(qnx4_inode->di_xblk); |
1da177e4c
|
87 88 89 90 91 92 |
ix = 0; while ( --nxtnt > 0 ) { if ( ix == 0 ) { // read next xtnt block. bh = sb_bread(inode->i_sb, i_xblk - 1); if ( !bh ) { |
891ddb95d
|
93 94 |
QNX4DEBUG((KERN_ERR "qnx4: I/O error reading xtnt block [%ld]) ", i_xblk - 1)); |
1da177e4c
|
95 96 97 98 |
return -EIO; } xblk = (struct qnx4_xblk*)bh->b_data; if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) { |
891ddb95d
|
99 100 |
QNX4DEBUG((KERN_ERR "qnx4: block at %ld is not a valid xtnt ", qnx4_inode->i_xblk)); |
1da177e4c
|
101 102 103 |
return -EIO; } } |
7cd916f6e
|
104 105 |
block = try_extent(&xblk->xblk_xtnts[ix], &offset); if (block) { |
1da177e4c
|
106 |
// got it! |
1da177e4c
|
107 108 |
break; } |
1da177e4c
|
109 110 111 112 113 114 115 116 117 118 |
if ( ++ix >= xblk->xblk_num_xtnts ) { i_xblk = le32_to_cpu(xblk->xblk_next_xblk); ix = 0; brelse( bh ); bh = NULL; } } if ( bh ) brelse( bh ); } |
891ddb95d
|
119 120 |
QNX4DEBUG((KERN_INFO "qnx4: mapping block %ld of inode %ld = %ld ",iblock,inode->i_ino,block)); |
1da177e4c
|
121 122 |
return block; } |
726c33422
|
123 |
static int qnx4_statfs(struct dentry *dentry, struct kstatfs *buf) |
1da177e4c
|
124 |
{ |
726c33422
|
125 |
struct super_block *sb = dentry->d_sb; |
5b76dc066
|
126 |
u64 id = huge_encode_dev(sb->s_bdev->bd_dev); |
726c33422
|
127 |
|
1da177e4c
|
128 129 130 131 132 133 |
buf->f_type = sb->s_magic; buf->f_bsize = sb->s_blocksize; buf->f_blocks = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8; buf->f_bfree = qnx4_count_free_blocks(sb); buf->f_bavail = buf->f_bfree; buf->f_namelen = QNX4_NAME_MAX; |
5b76dc066
|
134 135 |
buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); |
1da177e4c
|
136 |
|
1da177e4c
|
137 138 139 140 141 142 143 144 |
return 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. */ |
208adb640
|
145 146 |
static const char *qnx4_checkroot(struct super_block *sb, struct qnx4_super_block *s) |
1da177e4c
|
147 148 149 150 151 |
{ struct buffer_head *bh; struct qnx4_inode_entry *rootdir; int rd, rl; int i, j; |
1da177e4c
|
152 |
|
208adb640
|
153 |
if (s->RootDir.di_fname[0] != '/' || s->RootDir.di_fname[1] != '\0') |
1da177e4c
|
154 |
return "no qnx4 filesystem (no root dir)."; |
4134bf81f
|
155 156 |
QNX4DEBUG((KERN_NOTICE "QNX4 filesystem found on dev %s. ", sb->s_id)); |
208adb640
|
157 158 |
rd = le32_to_cpu(s->RootDir.di_first_xtnt.xtnt_blk) - 1; rl = le32_to_cpu(s->RootDir.di_first_xtnt.xtnt_size); |
4134bf81f
|
159 160 161 162 163 164 165 166 167 168 169 170 171 |
for (j = 0; j < rl; j++) { bh = sb_bread(sb, rd + j); /* root dir, first block */ if (bh == NULL) return "unable to read root entry."; rootdir = (struct qnx4_inode_entry *) bh->b_data; for (i = 0; i < QNX4_INODES_PER_BLOCK; i++, rootdir++) { QNX4DEBUG((KERN_INFO "rootdir entry found : [%s] ", rootdir->di_fname)); if (strcmp(rootdir->di_fname, QNX4_BMNAME) != 0) continue; qnx4_sb(sb)->BitMap = kmemdup(rootdir, sizeof(struct qnx4_inode_entry), GFP_KERNEL); |
1da177e4c
|
172 |
brelse(bh); |
4134bf81f
|
173 174 175 176 |
if (!qnx4_sb(sb)->BitMap) return "not enough memory for bitmap inode"; /* keep bitmap inode known */ return NULL; |
1da177e4c
|
177 |
} |
4134bf81f
|
178 |
brelse(bh); |
1da177e4c
|
179 |
} |
4134bf81f
|
180 |
return "bitmap file not found."; |
1da177e4c
|
181 182 183 184 185 186 187 188 |
} static int qnx4_fill_super(struct super_block *s, void *data, int silent) { struct buffer_head *bh; struct inode *root; const char *errmsg; struct qnx4_sb_info *qs; |
f8314dc60
|
189 |
qs = kzalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); |
073c21416
|
190 |
if (!qs) |
1da177e4c
|
191 192 |
return -ENOMEM; s->s_fs_info = qs; |
1da177e4c
|
193 194 |
sb_set_blocksize(s, QNX4_BLOCK_SIZE); |
208adb640
|
195 196 197 |
s->s_op = &qnx4_sops; s->s_magic = QNX4_SUPER_MAGIC; s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ |
1da177e4c
|
198 199 200 201 202 |
/* Check the superblock signature. Since the qnx4 code is dangerous, we should leave as quickly as possible if we don't belong here... */ bh = sb_bread(s, 1); if (!bh) { |
891ddb95d
|
203 204 |
printk(KERN_ERR "qnx4: unable to read the superblock "); |
208adb640
|
205 |
return -EINVAL; |
1da177e4c
|
206 |
} |
1da177e4c
|
207 208 |
/* check before allocating dentries, inodes, .. */ |
208adb640
|
209 210 |
errmsg = qnx4_checkroot(s, (struct qnx4_super_block *) bh->b_data); brelse(bh); |
1da177e4c
|
211 212 |
if (errmsg != NULL) { if (!silent) |
891ddb95d
|
213 214 |
printk(KERN_ERR "qnx4: %s ", errmsg); |
208adb640
|
215 |
return -EINVAL; |
1da177e4c
|
216 217 218 |
} /* does root not have inode number QNX4_ROOT_INO ?? */ |
2b7e5bcbd
|
219 220 |
root = qnx4_iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); if (IS_ERR(root)) { |
891ddb95d
|
221 222 |
printk(KERN_ERR "qnx4: get inode failed "); |
208adb640
|
223 |
return PTR_ERR(root); |
1da177e4c
|
224 |
} |
48fde701a
|
225 |
s->s_root = d_make_root(root); |
1da177e4c
|
226 |
if (s->s_root == NULL) |
208adb640
|
227 |
return -ENOMEM; |
1da177e4c
|
228 |
|
1da177e4c
|
229 |
return 0; |
1da177e4c
|
230 |
} |
208adb640
|
231 |
static void qnx4_kill_sb(struct super_block *sb) |
1da177e4c
|
232 233 |
{ struct qnx4_sb_info *qs = qnx4_sb(sb); |
208adb640
|
234 235 236 237 238 |
kill_block_super(sb); if (qs) { kfree(qs->BitMap); kfree(qs); } |
1da177e4c
|
239 |
} |
1da177e4c
|
240 241 242 243 |
static int qnx4_readpage(struct file *file, struct page *page) { return block_read_full_page(page,qnx4_get_block); } |
f87061842
|
244 |
|
1da177e4c
|
245 246 247 248 |
static sector_t qnx4_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,qnx4_get_block); } |
f5e54d6e5
|
249 |
static const struct address_space_operations qnx4_aops = { |
1da177e4c
|
250 |
.readpage = qnx4_readpage, |
1da177e4c
|
251 252 |
.bmap = qnx4_bmap }; |
2b7e5bcbd
|
253 |
struct inode *qnx4_iget(struct super_block *sb, unsigned long ino) |
1da177e4c
|
254 255 256 |
{ struct buffer_head *bh; struct qnx4_inode_entry *raw_inode; |
2b7e5bcbd
|
257 258 259 |
int block; struct qnx4_inode_entry *qnx4_inode; struct inode *inode; |
1da177e4c
|
260 |
|
2b7e5bcbd
|
261 262 263 264 265 266 267 |
inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; qnx4_inode = qnx4_raw_inode(inode); |
1da177e4c
|
268 |
inode->i_mode = 0; |
891ddb95d
|
269 270 |
QNX4DEBUG((KERN_INFO "reading inode : [%d] ", ino)); |
1da177e4c
|
271 |
if (!ino) { |
2b7e5bcbd
|
272 273 274 |
printk(KERN_ERR "qnx4: bad inode number on dev %s: %lu is " "out of range ", |
1da177e4c
|
275 |
sb->s_id, ino); |
2b7e5bcbd
|
276 277 |
iget_failed(inode); return ERR_PTR(-EIO); |
1da177e4c
|
278 279 280 281 |
} block = ino / QNX4_INODES_PER_BLOCK; if (!(bh = sb_bread(sb, block))) { |
891ddb95d
|
282 |
printk(KERN_ERR "qnx4: major problem: unable to read inode from dev " |
1da177e4c
|
283 284 |
"%s ", sb->s_id); |
2b7e5bcbd
|
285 286 |
iget_failed(inode); return ERR_PTR(-EIO); |
1da177e4c
|
287 288 289 290 291 |
} raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); inode->i_mode = le16_to_cpu(raw_inode->di_mode); |
511728d77
|
292 293 |
i_uid_write(inode, (uid_t)le16_to_cpu(raw_inode->di_uid)); i_gid_write(inode, (gid_t)le16_to_cpu(raw_inode->di_gid)); |
bfe868486
|
294 |
set_nlink(inode, le16_to_cpu(raw_inode->di_nlink)); |
1da177e4c
|
295 296 297 298 299 300 301 302 |
inode->i_size = le32_to_cpu(raw_inode->di_size); inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->di_mtime); inode->i_mtime.tv_nsec = 0; inode->i_atime.tv_sec = le32_to_cpu(raw_inode->di_atime); inode->i_atime.tv_nsec = 0; inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->di_ctime); inode->i_ctime.tv_nsec = 0; inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size); |
1da177e4c
|
303 304 305 |
memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE); if (S_ISREG(inode->i_mode)) { |
945ffe54b
|
306 |
inode->i_fop = &generic_ro_fops; |
1da177e4c
|
307 308 309 310 311 312 313 |
inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &qnx4_dir_inode_operations; inode->i_fop = &qnx4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; |
21fc61c73
|
314 |
inode_nohighmem(inode); |
1da177e4c
|
315 316 |
inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; |
2b7e5bcbd
|
317 318 319 320 321 322 323 324 |
} else { printk(KERN_ERR "qnx4: bad inode %lu on dev %s ", ino, sb->s_id); iget_failed(inode); brelse(bh); return ERR_PTR(-EIO); } |
1da177e4c
|
325 |
brelse(bh); |
2b7e5bcbd
|
326 327 |
unlock_new_inode(inode); return inode; |
1da177e4c
|
328 |
} |
e18b890bb
|
329 |
static struct kmem_cache *qnx4_inode_cachep; |
1da177e4c
|
330 331 332 333 |
static struct inode *qnx4_alloc_inode(struct super_block *sb) { struct qnx4_inode_info *ei; |
e94b17660
|
334 |
ei = kmem_cache_alloc(qnx4_inode_cachep, GFP_KERNEL); |
1da177e4c
|
335 336 337 338 |
if (!ei) return NULL; return &ei->vfs_inode; } |
fa0d7e3de
|
339 |
static void qnx4_i_callback(struct rcu_head *head) |
1da177e4c
|
340 |
{ |
fa0d7e3de
|
341 |
struct inode *inode = container_of(head, struct inode, i_rcu); |
1da177e4c
|
342 343 |
kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); } |
fa0d7e3de
|
344 345 346 347 |
static void qnx4_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, qnx4_i_callback); } |
51cc50685
|
348 |
static void init_once(void *foo) |
1da177e4c
|
349 350 |
{ struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; |
a35afb830
|
351 |
inode_init_once(&ei->vfs_inode); |
1da177e4c
|
352 353 354 355 356 357 |
} static int init_inodecache(void) { qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache", sizeof(struct qnx4_inode_info), |
fffb60f93
|
358 |
0, (SLAB_RECLAIM_ACCOUNT| |
5d097056c
|
359 |
SLAB_MEM_SPREAD|SLAB_ACCOUNT), |
20c2df83d
|
360 |
init_once); |
1da177e4c
|
361 362 363 364 365 366 367 |
if (qnx4_inode_cachep == NULL) return -ENOMEM; return 0; } static void destroy_inodecache(void) { |
8c0a85377
|
368 369 370 371 372 |
/* * Make sure all delayed rcu free inodes are flushed before we * destroy cache. */ rcu_barrier(); |
1a1d92c10
|
373 |
kmem_cache_destroy(qnx4_inode_cachep); |
1da177e4c
|
374 |
} |
152a08366
|
375 376 |
static struct dentry *qnx4_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) |
1da177e4c
|
377 |
{ |
152a08366
|
378 |
return mount_bdev(fs_type, flags, dev_name, data, qnx4_fill_super); |
1da177e4c
|
379 380 381 382 383 |
} static struct file_system_type qnx4_fs_type = { .owner = THIS_MODULE, .name = "qnx4", |
152a08366
|
384 |
.mount = qnx4_mount, |
208adb640
|
385 |
.kill_sb = qnx4_kill_sb, |
1da177e4c
|
386 387 |
.fs_flags = FS_REQUIRES_DEV, }; |
7f78e0351
|
388 |
MODULE_ALIAS_FS("qnx4"); |
1da177e4c
|
389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
static int __init init_qnx4_fs(void) { int err; err = init_inodecache(); if (err) return err; err = register_filesystem(&qnx4_fs_type); if (err) { destroy_inodecache(); return err; } |
891ddb95d
|
403 404 |
printk(KERN_INFO "QNX4 filesystem 0.2.3 registered. "); |
1da177e4c
|
405 406 407 408 409 410 411 412 413 414 415 416 |
return 0; } static void __exit exit_qnx4_fs(void) { unregister_filesystem(&qnx4_fs_type); destroy_inodecache(); } module_init(init_qnx4_fs) module_exit(exit_qnx4_fs) MODULE_LICENSE("GPL"); |