aboutsummaryrefslogtreecommitdiffstats
path: root/fs/adfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/adfs
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/adfs')
-rw-r--r--fs/adfs/Makefile7
-rw-r--r--fs/adfs/adfs.h127
-rw-r--r--fs/adfs/dir.c302
-rw-r--r--fs/adfs/dir_f.c460
-rw-r--r--fs/adfs/dir_f.h65
-rw-r--r--fs/adfs/dir_fplus.c179
-rw-r--r--fs/adfs/dir_fplus.h45
-rw-r--r--fs/adfs/file.c43
-rw-r--r--fs/adfs/inode.c395
-rw-r--r--fs/adfs/map.c296
-rw-r--r--fs/adfs/super.c508
11 files changed, 2427 insertions, 0 deletions
diff --git a/fs/adfs/Makefile b/fs/adfs/Makefile
new file mode 100644
index 000000000000..9b2d71a9a35c
--- /dev/null
+++ b/fs/adfs/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for the linux adfs filesystem routines.
3#
4
5obj-$(CONFIG_ADFS_FS) += adfs.o
6
7adfs-objs := dir.o dir_f.o dir_fplus.o file.o inode.o map.o super.o
diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h
new file mode 100644
index 000000000000..63f5df9afb71
--- /dev/null
+++ b/fs/adfs/adfs.h
@@ -0,0 +1,127 @@
1/* Internal data structures for ADFS */
2
3#define ADFS_FREE_FRAG 0
4#define ADFS_BAD_FRAG 1
5#define ADFS_ROOT_FRAG 2
6
7#define ADFS_NDA_OWNER_READ (1 << 0)
8#define ADFS_NDA_OWNER_WRITE (1 << 1)
9#define ADFS_NDA_LOCKED (1 << 2)
10#define ADFS_NDA_DIRECTORY (1 << 3)
11#define ADFS_NDA_EXECUTE (1 << 4)
12#define ADFS_NDA_PUBLIC_READ (1 << 5)
13#define ADFS_NDA_PUBLIC_WRITE (1 << 6)
14
15#include <linux/version.h>
16#include "dir_f.h"
17
18struct buffer_head;
19
20/*
21 * Directory handling
22 */
23struct adfs_dir {
24 struct super_block *sb;
25
26 int nr_buffers;
27 struct buffer_head *bh[4];
28 unsigned int pos;
29 unsigned int parent_id;
30
31 struct adfs_dirheader dirhead;
32 union adfs_dirtail dirtail;
33};
34
35/*
36 * This is the overall maximum name length
37 */
38#define ADFS_MAX_NAME_LEN 256
39struct object_info {
40 __u32 parent_id; /* parent object id */
41 __u32 file_id; /* object id */
42 __u32 loadaddr; /* load address */
43 __u32 execaddr; /* execution address */
44 __u32 size; /* size */
45 __u8 attr; /* RISC OS attributes */
46 unsigned char name_len; /* name length */
47 char name[ADFS_MAX_NAME_LEN];/* file name */
48};
49
50struct adfs_dir_ops {
51 int (*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);
52 int (*setpos)(struct adfs_dir *dir, unsigned int fpos);
53 int (*getnext)(struct adfs_dir *dir, struct object_info *obj);
54 int (*update)(struct adfs_dir *dir, struct object_info *obj);
55 int (*create)(struct adfs_dir *dir, struct object_info *obj);
56 int (*remove)(struct adfs_dir *dir, struct object_info *obj);
57 void (*free)(struct adfs_dir *dir);
58};
59
60struct adfs_discmap {
61 struct buffer_head *dm_bh;
62 __u32 dm_startblk;
63 unsigned int dm_startbit;
64 unsigned int dm_endbit;
65};
66
67/* Inode stuff */
68struct inode *adfs_iget(struct super_block *sb, struct object_info *obj);
69int adfs_write_inode(struct inode *inode,int unused);
70int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
71
72/* map.c */
73extern int adfs_map_lookup(struct super_block *sb, unsigned int frag_id, unsigned int offset);
74extern unsigned int adfs_map_free(struct super_block *sb);
75
76/* Misc */
77void __adfs_error(struct super_block *sb, const char *function,
78 const char *fmt, ...);
79#define adfs_error(sb, fmt...) __adfs_error(sb, __FUNCTION__, fmt)
80
81/* super.c */
82
83/*
84 * Inodes and file operations
85 */
86
87/* dir_*.c */
88extern struct inode_operations adfs_dir_inode_operations;
89extern struct file_operations adfs_dir_operations;
90extern struct dentry_operations adfs_dentry_operations;
91extern struct adfs_dir_ops adfs_f_dir_ops;
92extern struct adfs_dir_ops adfs_fplus_dir_ops;
93
94extern int adfs_dir_update(struct super_block *sb, struct object_info *obj);
95
96/* file.c */
97extern struct inode_operations adfs_file_inode_operations;
98extern struct file_operations adfs_file_operations;
99
100extern inline __u32 signed_asl(__u32 val, signed int shift)
101{
102 if (shift >= 0)
103 val <<= shift;
104 else
105 val >>= -shift;
106 return val;
107}
108
109/*
110 * Calculate the address of a block in an object given the block offset
111 * and the object identity.
112 *
113 * The root directory ID should always be looked up in the map [3.4]
114 */
115extern inline int
116__adfs_block_map(struct super_block *sb, unsigned int object_id,
117 unsigned int block)
118{
119 if (object_id & 255) {
120 unsigned int off;
121
122 off = (object_id & 255) - 1;
123 block += off << ADFS_SB(sb)->s_log2sharesize;
124 }
125
126 return adfs_map_lookup(sb, object_id >> 8, block);
127}
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
new file mode 100644
index 000000000000..0b4c3a028076
--- /dev/null
+++ b/fs/adfs/dir.c
@@ -0,0 +1,302 @@
1/*
2 * linux/fs/adfs/dir.c
3 *
4 * Copyright (C) 1999-2000 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Common directory handling for ADFS
11 */
12#include <linux/config.h>
13#include <linux/errno.h>
14#include <linux/fs.h>
15#include <linux/adfs_fs.h>
16#include <linux/time.h>
17#include <linux/stat.h>
18#include <linux/spinlock.h>
19#include <linux/smp_lock.h>
20#include <linux/buffer_head.h> /* for file_fsync() */
21
22#include "adfs.h"
23
24/*
25 * For future. This should probably be per-directory.
26 */
27static DEFINE_RWLOCK(adfs_dir_lock);
28
29static int
30adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
31{
32 struct inode *inode = filp->f_dentry->d_inode;
33 struct super_block *sb = inode->i_sb;
34 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
35 struct object_info obj;
36 struct adfs_dir dir;
37 int ret = 0;
38
39 lock_kernel();
40
41 if (filp->f_pos >> 32)
42 goto out;
43
44 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
45 if (ret)
46 goto out;
47
48 switch ((unsigned long)filp->f_pos) {
49 case 0:
50 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
51 goto free_out;
52 filp->f_pos += 1;
53
54 case 1:
55 if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0)
56 goto free_out;
57 filp->f_pos += 1;
58
59 default:
60 break;
61 }
62
63 read_lock(&adfs_dir_lock);
64
65 ret = ops->setpos(&dir, filp->f_pos - 2);
66 if (ret)
67 goto unlock_out;
68 while (ops->getnext(&dir, &obj) == 0) {
69 if (filldir(dirent, obj.name, obj.name_len,
70 filp->f_pos, obj.file_id, DT_UNKNOWN) < 0)
71 goto unlock_out;
72 filp->f_pos += 1;
73 }
74
75unlock_out:
76 read_unlock(&adfs_dir_lock);
77
78free_out:
79 ops->free(&dir);
80
81out:
82 unlock_kernel();
83 return ret;
84}
85
86int
87adfs_dir_update(struct super_block *sb, struct object_info *obj)
88{
89 int ret = -EINVAL;
90#ifdef CONFIG_ADFS_FS_RW
91 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
92 struct adfs_dir dir;
93
94 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
95 obj->file_id, obj->parent_id);
96
97 if (!ops->update) {
98 ret = -EINVAL;
99 goto out;
100 }
101
102 ret = ops->read(sb, obj->parent_id, 0, &dir);
103 if (ret)
104 goto out;
105
106 write_lock(&adfs_dir_lock);
107 ret = ops->update(&dir, obj);
108 write_unlock(&adfs_dir_lock);
109
110 ops->free(&dir);
111out:
112#endif
113 return ret;
114}
115
116static int
117adfs_match(struct qstr *name, struct object_info *obj)
118{
119 int i;
120
121 if (name->len != obj->name_len)
122 return 0;
123
124 for (i = 0; i < name->len; i++) {
125 char c1, c2;
126
127 c1 = name->name[i];
128 c2 = obj->name[i];
129
130 if (c1 >= 'A' && c1 <= 'Z')
131 c1 += 'a' - 'A';
132 if (c2 >= 'A' && c2 <= 'Z')
133 c2 += 'a' - 'A';
134
135 if (c1 != c2)
136 return 0;
137 }
138 return 1;
139}
140
141static int
142adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
143{
144 struct super_block *sb = inode->i_sb;
145 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
146 struct adfs_dir dir;
147 int ret;
148
149 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
150 if (ret)
151 goto out;
152
153 if (ADFS_I(inode)->parent_id != dir.parent_id) {
154 adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
155 ADFS_I(inode)->parent_id, dir.parent_id);
156 ret = -EIO;
157 goto free_out;
158 }
159
160 obj->parent_id = inode->i_ino;
161
162 /*
163 * '.' is handled by reserved_lookup() in fs/namei.c
164 */
165 if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
166 /*
167 * Currently unable to fill in the rest of 'obj',
168 * but this is better than nothing. We need to
169 * ascend one level to find it's parent.
170 */
171 obj->name_len = 0;
172 obj->file_id = obj->parent_id;
173 goto free_out;
174 }
175
176 read_lock(&adfs_dir_lock);
177
178 ret = ops->setpos(&dir, 0);
179 if (ret)
180 goto unlock_out;
181
182 ret = -ENOENT;
183 while (ops->getnext(&dir, obj) == 0) {
184 if (adfs_match(name, obj)) {
185 ret = 0;
186 break;
187 }
188 }
189
190unlock_out:
191 read_unlock(&adfs_dir_lock);
192
193free_out:
194 ops->free(&dir);
195out:
196 return ret;
197}
198
199struct file_operations adfs_dir_operations = {
200 .read = generic_read_dir,
201 .readdir = adfs_readdir,
202 .fsync = file_fsync,
203};
204
205static int
206adfs_hash(struct dentry *parent, struct qstr *qstr)
207{
208 const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
209 const unsigned char *name;
210 unsigned long hash;
211 int i;
212
213 if (qstr->len < name_len)
214 return 0;
215
216 /*
217 * Truncate the name in place, avoids
218 * having to define a compare function.
219 */
220 qstr->len = i = name_len;
221 name = qstr->name;
222 hash = init_name_hash();
223 while (i--) {
224 char c;
225
226 c = *name++;
227 if (c >= 'A' && c <= 'Z')
228 c += 'a' - 'A';
229
230 hash = partial_name_hash(c, hash);
231 }
232 qstr->hash = end_name_hash(hash);
233
234 return 0;
235}
236
237/*
238 * Compare two names, taking note of the name length
239 * requirements of the underlying filesystem.
240 */
241static int
242adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
243{
244 int i;
245
246 if (entry->len != name->len)
247 return 1;
248
249 for (i = 0; i < name->len; i++) {
250 char a, b;
251
252 a = entry->name[i];
253 b = name->name[i];
254
255 if (a >= 'A' && a <= 'Z')
256 a += 'a' - 'A';
257 if (b >= 'A' && b <= 'Z')
258 b += 'a' - 'A';
259
260 if (a != b)
261 return 1;
262 }
263 return 0;
264}
265
266struct dentry_operations adfs_dentry_operations = {
267 .d_hash = adfs_hash,
268 .d_compare = adfs_compare,
269};
270
271static struct dentry *
272adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
273{
274 struct inode *inode = NULL;
275 struct object_info obj;
276 int error;
277
278 dentry->d_op = &adfs_dentry_operations;
279 lock_kernel();
280 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
281 if (error == 0) {
282 error = -EACCES;
283 /*
284 * This only returns NULL if get_empty_inode
285 * fails.
286 */
287 inode = adfs_iget(dir->i_sb, &obj);
288 if (inode)
289 error = 0;
290 }
291 unlock_kernel();
292 d_add(dentry, inode);
293 return ERR_PTR(error);
294}
295
296/*
297 * directories can handle most operations...
298 */
299struct inode_operations adfs_dir_inode_operations = {
300 .lookup = adfs_lookup,
301 .setattr = adfs_notify_change,
302};
diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c
new file mode 100644
index 000000000000..bbfc86259272
--- /dev/null
+++ b/fs/adfs/dir_f.c
@@ -0,0 +1,460 @@
1/*
2 * linux/fs/adfs/dir_f.c
3 *
4 * Copyright (C) 1997-1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * E and F format directory handling
11 */
12#include <linux/errno.h>
13#include <linux/fs.h>
14#include <linux/adfs_fs.h>
15#include <linux/time.h>
16#include <linux/stat.h>
17#include <linux/spinlock.h>
18#include <linux/buffer_head.h>
19#include <linux/string.h>
20
21#include "adfs.h"
22#include "dir_f.h"
23
24static void adfs_f_free(struct adfs_dir *dir);
25
26/*
27 * Read an (unaligned) value of length 1..4 bytes
28 */
29static inline unsigned int adfs_readval(unsigned char *p, int len)
30{
31 unsigned int val = 0;
32
33 switch (len) {
34 case 4: val |= p[3] << 24;
35 case 3: val |= p[2] << 16;
36 case 2: val |= p[1] << 8;
37 default: val |= p[0];
38 }
39 return val;
40}
41
42static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
43{
44 switch (len) {
45 case 4: p[3] = val >> 24;
46 case 3: p[2] = val >> 16;
47 case 2: p[1] = val >> 8;
48 default: p[0] = val;
49 }
50}
51
52static inline int adfs_readname(char *buf, char *ptr, int maxlen)
53{
54 char *old_buf = buf;
55
56 while (*ptr >= ' ' && maxlen--) {
57 if (*ptr == '/')
58 *buf++ = '.';
59 else
60 *buf++ = *ptr;
61 ptr++;
62 }
63 *buf = '\0';
64
65 return buf - old_buf;
66}
67
68#define ror13(v) ((v >> 13) | (v << 19))
69
70#define dir_u8(idx) \
71 ({ int _buf = idx >> blocksize_bits; \
72 int _off = idx - (_buf << blocksize_bits);\
73 *(u8 *)(bh[_buf]->b_data + _off); \
74 })
75
76#define dir_u32(idx) \
77 ({ int _buf = idx >> blocksize_bits; \
78 int _off = idx - (_buf << blocksize_bits);\
79 *(__le32 *)(bh[_buf]->b_data + _off); \
80 })
81
82#define bufoff(_bh,_idx) \
83 ({ int _buf = _idx >> blocksize_bits; \
84 int _off = _idx - (_buf << blocksize_bits);\
85 (u8 *)(_bh[_buf]->b_data + _off); \
86 })
87
88/*
89 * There are some algorithms that are nice in
90 * assembler, but a bitch in C... This is one
91 * of them.
92 */
93static u8
94adfs_dir_checkbyte(const struct adfs_dir *dir)
95{
96 struct buffer_head * const *bh = dir->bh;
97 const int blocksize_bits = dir->sb->s_blocksize_bits;
98 union { __le32 *ptr32; u8 *ptr8; } ptr, end;
99 u32 dircheck = 0;
100 int last = 5 - 26;
101 int i = 0;
102
103 /*
104 * Accumulate each word up to the last whole
105 * word of the last directory entry. This
106 * can spread across several buffer heads.
107 */
108 do {
109 last += 26;
110 do {
111 dircheck = le32_to_cpu(dir_u32(i)) ^ ror13(dircheck);
112
113 i += sizeof(u32);
114 } while (i < (last & ~3));
115 } while (dir_u8(last) != 0);
116
117 /*
118 * Accumulate the last few bytes. These
119 * bytes will be within the same bh.
120 */
121 if (i != last) {
122 ptr.ptr8 = bufoff(bh, i);
123 end.ptr8 = ptr.ptr8 + last - i;
124
125 do
126 dircheck = *ptr.ptr8++ ^ ror13(dircheck);
127 while (ptr.ptr8 < end.ptr8);
128 }
129
130 /*
131 * The directory tail is in the final bh
132 * Note that contary to the RISC OS PRMs,
133 * the first few bytes are NOT included
134 * in the check. All bytes are in the
135 * same bh.
136 */
137 ptr.ptr8 = bufoff(bh, 2008);
138 end.ptr8 = ptr.ptr8 + 36;
139
140 do {
141 __le32 v = *ptr.ptr32++;
142 dircheck = le32_to_cpu(v) ^ ror13(dircheck);
143 } while (ptr.ptr32 < end.ptr32);
144
145 return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
146}
147
148/*
149 * Read and check that a directory is valid
150 */
151static int
152adfs_dir_read(struct super_block *sb, unsigned long object_id,
153 unsigned int size, struct adfs_dir *dir)
154{
155 const unsigned int blocksize_bits = sb->s_blocksize_bits;
156 int blk = 0;
157
158 /*
159 * Directories which are not a multiple of 2048 bytes
160 * are considered bad v2 [3.6]
161 */
162 if (size & 2047)
163 goto bad_dir;
164
165 size >>= blocksize_bits;
166
167 dir->nr_buffers = 0;
168 dir->sb = sb;
169
170 for (blk = 0; blk < size; blk++) {
171 int phys;
172
173 phys = __adfs_block_map(sb, object_id, blk);
174 if (!phys) {
175 adfs_error(sb, "dir object %lX has a hole at offset %d",
176 object_id, blk);
177 goto release_buffers;
178 }
179
180 dir->bh[blk] = sb_bread(sb, phys);
181 if (!dir->bh[blk])
182 goto release_buffers;
183 }
184
185 memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
186 memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
187
188 if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
189 memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
190 goto bad_dir;
191
192 if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
193 memcmp(&dir->dirhead.startname, "Hugo", 4))
194 goto bad_dir;
195
196 if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
197 goto bad_dir;
198
199 dir->nr_buffers = blk;
200
201 return 0;
202
203bad_dir:
204 adfs_error(sb, "corrupted directory fragment %lX",
205 object_id);
206release_buffers:
207 for (blk -= 1; blk >= 0; blk -= 1)
208 brelse(dir->bh[blk]);
209
210 dir->sb = NULL;
211
212 return -EIO;
213}
214
215/*
216 * convert a disk-based directory entry to a Linux ADFS directory entry
217 */
218static inline void
219adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
220{
221 obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
222 obj->file_id = adfs_readval(de->dirinddiscadd, 3);
223 obj->loadaddr = adfs_readval(de->dirload, 4);
224 obj->execaddr = adfs_readval(de->direxec, 4);
225 obj->size = adfs_readval(de->dirlen, 4);
226 obj->attr = de->newdiratts;
227}
228
229/*
230 * convert a Linux ADFS directory entry to a disk-based directory entry
231 */
232static inline void
233adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
234{
235 adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
236 adfs_writeval(de->dirload, 4, obj->loadaddr);
237 adfs_writeval(de->direxec, 4, obj->execaddr);
238 adfs_writeval(de->dirlen, 4, obj->size);
239 de->newdiratts = obj->attr;
240}
241
242/*
243 * get a directory entry. Note that the caller is responsible
244 * for holding the relevant locks.
245 */
246static int
247__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
248{
249 struct super_block *sb = dir->sb;
250 struct adfs_direntry de;
251 int thissize, buffer, offset;
252
253 buffer = pos >> sb->s_blocksize_bits;
254
255 if (buffer > dir->nr_buffers)
256 return -EINVAL;
257
258 offset = pos & (sb->s_blocksize - 1);
259 thissize = sb->s_blocksize - offset;
260 if (thissize > 26)
261 thissize = 26;
262
263 memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
264 if (thissize != 26)
265 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
266 26 - thissize);
267
268 if (!de.dirobname[0])
269 return -ENOENT;
270
271 adfs_dir2obj(obj, &de);
272
273 return 0;
274}
275
276static int
277__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
278{
279 struct super_block *sb = dir->sb;
280 struct adfs_direntry de;
281 int thissize, buffer, offset;
282
283 buffer = pos >> sb->s_blocksize_bits;
284
285 if (buffer > dir->nr_buffers)
286 return -EINVAL;
287
288 offset = pos & (sb->s_blocksize - 1);
289 thissize = sb->s_blocksize - offset;
290 if (thissize > 26)
291 thissize = 26;
292
293 /*
294 * Get the entry in total
295 */
296 memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
297 if (thissize != 26)
298 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
299 26 - thissize);
300
301 /*
302 * update it
303 */
304 adfs_obj2dir(&de, obj);
305
306 /*
307 * Put the new entry back
308 */
309 memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
310 if (thissize != 26)
311 memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
312 26 - thissize);
313
314 return 0;
315}
316
317/*
318 * the caller is responsible for holding the necessary
319 * locks.
320 */
321static int
322adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
323{
324 int pos, ret;
325
326 ret = -ENOENT;
327
328 for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
329 struct object_info obj;
330
331 if (!__adfs_dir_get(dir, pos, &obj))
332 break;
333
334 if (obj.file_id == object_id) {
335 ret = pos;
336 break;
337 }
338 }
339
340 return ret;
341}
342
343static int
344adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
345{
346 int ret;
347
348 if (sz != ADFS_NEWDIR_SIZE)
349 return -EIO;
350
351 ret = adfs_dir_read(sb, id, sz, dir);
352 if (ret)
353 adfs_error(sb, "unable to read directory");
354 else
355 dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
356
357 return ret;
358}
359
360static int
361adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
362{
363 if (fpos >= ADFS_NUM_DIR_ENTRIES)
364 return -ENOENT;
365
366 dir->pos = 5 + fpos * 26;
367 return 0;
368}
369
370static int
371adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
372{
373 unsigned int ret;
374
375 ret = __adfs_dir_get(dir, dir->pos, obj);
376 if (ret == 0)
377 dir->pos += 26;
378
379 return ret;
380}
381
382static int
383adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
384{
385 struct super_block *sb = dir->sb;
386 int ret, i;
387
388 ret = adfs_dir_find_entry(dir, obj->file_id);
389 if (ret < 0) {
390 adfs_error(dir->sb, "unable to locate entry to update");
391 goto out;
392 }
393
394 __adfs_dir_put(dir, ret, obj);
395
396 /*
397 * Increment directory sequence number
398 */
399 dir->bh[0]->b_data[0] += 1;
400 dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
401
402 ret = adfs_dir_checkbyte(dir);
403 /*
404 * Update directory check byte
405 */
406 dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
407
408#if 1
409 {
410 const unsigned int blocksize_bits = sb->s_blocksize_bits;
411
412 memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
413 memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
414
415 if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
416 memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
417 goto bad_dir;
418
419 if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
420 memcmp(&dir->dirhead.startname, "Hugo", 4))
421 goto bad_dir;
422
423 if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
424 goto bad_dir;
425 }
426#endif
427 for (i = dir->nr_buffers - 1; i >= 0; i--)
428 mark_buffer_dirty(dir->bh[i]);
429
430 ret = 0;
431out:
432 return ret;
433#if 1
434bad_dir:
435 adfs_error(dir->sb, "whoops! I broke a directory!");
436 return -EIO;
437#endif
438}
439
440static void
441adfs_f_free(struct adfs_dir *dir)
442{
443 int i;
444
445 for (i = dir->nr_buffers - 1; i >= 0; i--) {
446 brelse(dir->bh[i]);
447 dir->bh[i] = NULL;
448 }
449
450 dir->nr_buffers = 0;
451 dir->sb = NULL;
452}
453
454struct adfs_dir_ops adfs_f_dir_ops = {
455 .read = adfs_f_read,
456 .setpos = adfs_f_setpos,
457 .getnext = adfs_f_getnext,
458 .update = adfs_f_update,
459 .free = adfs_f_free
460};
diff --git a/fs/adfs/dir_f.h b/fs/adfs/dir_f.h
new file mode 100644
index 000000000000..e4713404096c
--- /dev/null
+++ b/fs/adfs/dir_f.h
@@ -0,0 +1,65 @@
1/*
2 * linux/fs/adfs/dir_f.h
3 *
4 * Copyright (C) 1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Structures of directories on the F format disk
11 */
12#ifndef ADFS_DIR_F_H
13#define ADFS_DIR_F_H
14
15/*
16 * Directory header
17 */
18struct adfs_dirheader {
19 unsigned char startmasseq;
20 unsigned char startname[4];
21};
22
23#define ADFS_NEWDIR_SIZE 2048
24#define ADFS_NUM_DIR_ENTRIES 77
25
26/*
27 * Directory entries
28 */
29struct adfs_direntry {
30#define ADFS_F_NAME_LEN 10
31 char dirobname[ADFS_F_NAME_LEN];
32 __u8 dirload[4];
33 __u8 direxec[4];
34 __u8 dirlen[4];
35 __u8 dirinddiscadd[3];
36 __u8 newdiratts;
37};
38
39/*
40 * Directory tail
41 */
42union adfs_dirtail {
43 struct {
44 unsigned char dirlastmask;
45 char dirname[10];
46 unsigned char dirparent[3];
47 char dirtitle[19];
48 unsigned char reserved[14];
49 unsigned char endmasseq;
50 unsigned char endname[4];
51 unsigned char dircheckbyte;
52 } old;
53 struct {
54 unsigned char dirlastmask;
55 unsigned char reserved[2];
56 unsigned char dirparent[3];
57 char dirtitle[19];
58 char dirname[10];
59 unsigned char endmasseq;
60 unsigned char endname[4];
61 unsigned char dircheckbyte;
62 } new;
63};
64
65#endif
diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c
new file mode 100644
index 000000000000..1ec644e32df9
--- /dev/null
+++ b/fs/adfs/dir_fplus.c
@@ -0,0 +1,179 @@
1/*
2 * linux/fs/adfs/dir_fplus.c
3 *
4 * Copyright (C) 1997-1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/errno.h>
11#include <linux/fs.h>
12#include <linux/adfs_fs.h>
13#include <linux/time.h>
14#include <linux/stat.h>
15#include <linux/spinlock.h>
16#include <linux/buffer_head.h>
17#include <linux/string.h>
18
19#include "adfs.h"
20#include "dir_fplus.h"
21
22static int
23adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
24{
25 struct adfs_bigdirheader *h;
26 struct adfs_bigdirtail *t;
27 unsigned long block;
28 unsigned int blk, size;
29 int i, ret = -EIO;
30
31 dir->nr_buffers = 0;
32
33 block = __adfs_block_map(sb, id, 0);
34 if (!block) {
35 adfs_error(sb, "dir object %X has a hole at offset 0", id);
36 goto out;
37 }
38
39 dir->bh[0] = sb_bread(sb, block);
40 if (!dir->bh[0])
41 goto out;
42 dir->nr_buffers += 1;
43
44 h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
45 size = le32_to_cpu(h->bigdirsize);
46 if (size != sz) {
47 printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n"
48 " does not match directory size\n");
49 }
50
51 if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
52 h->bigdirversion[2] != 0 || size & 2047 ||
53 h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME))
54 goto out;
55
56 size >>= sb->s_blocksize_bits;
57 for (blk = 1; blk < size; blk++) {
58 block = __adfs_block_map(sb, id, blk);
59 if (!block) {
60 adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
61 goto out;
62 }
63
64 dir->bh[blk] = sb_bread(sb, block);
65 if (!dir->bh[blk])
66 goto out;
67 dir->nr_buffers = blk;
68 }
69
70 t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8));
71
72 if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
73 t->bigdirendmasseq != h->startmasseq ||
74 t->reserved[0] != 0 || t->reserved[1] != 0)
75 goto out;
76
77 dir->parent_id = le32_to_cpu(h->bigdirparent);
78 dir->sb = sb;
79 return 0;
80out:
81 for (i = 0; i < dir->nr_buffers; i++)
82 brelse(dir->bh[i]);
83 dir->sb = NULL;
84 return ret;
85}
86
87static int
88adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
89{
90 struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
91 int ret = -ENOENT;
92
93 if (fpos <= le32_to_cpu(h->bigdirentries)) {
94 dir->pos = fpos;
95 ret = 0;
96 }
97
98 return ret;
99}
100
101static void
102dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
103{
104 struct super_block *sb = dir->sb;
105 unsigned int buffer, partial, remainder;
106
107 buffer = offset >> sb->s_blocksize_bits;
108 offset &= sb->s_blocksize - 1;
109
110 partial = sb->s_blocksize - offset;
111
112 if (partial >= len)
113 memcpy(to, dir->bh[buffer]->b_data + offset, len);
114 else {
115 char *c = (char *)to;
116
117 remainder = len - partial;
118
119 memcpy(c, dir->bh[buffer]->b_data + offset, partial);
120 memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder);
121 }
122}
123
124static int
125adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
126{
127 struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
128 struct adfs_bigdirentry bde;
129 unsigned int offset;
130 int i, ret = -ENOENT;
131
132 if (dir->pos >= le32_to_cpu(h->bigdirentries))
133 goto out;
134
135 offset = offsetof(struct adfs_bigdirheader, bigdirname);
136 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
137 offset += dir->pos * sizeof(struct adfs_bigdirentry);
138
139 dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
140
141 obj->loadaddr = le32_to_cpu(bde.bigdirload);
142 obj->execaddr = le32_to_cpu(bde.bigdirexec);
143 obj->size = le32_to_cpu(bde.bigdirlen);
144 obj->file_id = le32_to_cpu(bde.bigdirindaddr);
145 obj->attr = le32_to_cpu(bde.bigdirattr);
146 obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
147
148 offset = offsetof(struct adfs_bigdirheader, bigdirname);
149 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
150 offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
151 offset += le32_to_cpu(bde.bigdirobnameptr);
152
153 dir_memcpy(dir, offset, obj->name, obj->name_len);
154 for (i = 0; i < obj->name_len; i++)
155 if (obj->name[i] == '/')
156 obj->name[i] = '.';
157
158 dir->pos += 1;
159 ret = 0;
160out:
161 return ret;
162}
163
164static void
165adfs_fplus_free(struct adfs_dir *dir)
166{
167 int i;
168
169 for (i = 0; i < dir->nr_buffers; i++)
170 brelse(dir->bh[i]);
171 dir->sb = NULL;
172}
173
174struct adfs_dir_ops adfs_fplus_dir_ops = {
175 .read = adfs_fplus_read,
176 .setpos = adfs_fplus_setpos,
177 .getnext = adfs_fplus_getnext,
178 .free = adfs_fplus_free
179};
diff --git a/fs/adfs/dir_fplus.h b/fs/adfs/dir_fplus.h
new file mode 100644
index 000000000000..b55aa41a68fe
--- /dev/null
+++ b/fs/adfs/dir_fplus.h
@@ -0,0 +1,45 @@
1/*
2 * linux/fs/adfs/dir_fplus.h
3 *
4 * Copyright (C) 1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Structures of directories on the F+ format disk
11 */
12
13#define ADFS_FPLUS_NAME_LEN 255
14
15#define BIGDIRSTARTNAME ('S' | 'B' << 8 | 'P' << 16 | 'r' << 24)
16#define BIGDIRENDNAME ('o' | 'v' << 8 | 'e' << 16 | 'n' << 24)
17
18struct adfs_bigdirheader {
19 __u8 startmasseq;
20 __u8 bigdirversion[3];
21 __le32 bigdirstartname;
22 __le32 bigdirnamelen;
23 __le32 bigdirsize;
24 __le32 bigdirentries;
25 __le32 bigdirnamesize;
26 __le32 bigdirparent;
27 char bigdirname[1];
28};
29
30struct adfs_bigdirentry {
31 __le32 bigdirload;
32 __le32 bigdirexec;
33 __le32 bigdirlen;
34 __le32 bigdirindaddr;
35 __le32 bigdirattr;
36 __le32 bigdirobnamelen;
37 __le32 bigdirobnameptr;
38};
39
40struct adfs_bigdirtail {
41 __le32 bigdirendname;
42 __u8 bigdirendmasseq;
43 __u8 reserved[2];
44 __u8 bigdircheckbyte;
45};
diff --git a/fs/adfs/file.c b/fs/adfs/file.c
new file mode 100644
index 000000000000..afebbfde6968
--- /dev/null
+++ b/fs/adfs/file.c
@@ -0,0 +1,43 @@
1/*
2 * linux/fs/adfs/file.c
3 *
4 * Copyright (C) 1997-1999 Russell King
5 * from:
6 *
7 * linux/fs/ext2/file.c
8 *
9 * Copyright (C) 1992, 1993, 1994, 1995
10 * Remy Card (card@masi.ibp.fr)
11 * Laboratoire MASI - Institut Blaise Pascal
12 * Universite Pierre et Marie Curie (Paris VI)
13 *
14 * from
15 *
16 * linux/fs/minix/file.c
17 *
18 * Copyright (C) 1991, 1992 Linus Torvalds
19 *
20 * adfs regular file handling primitives
21 */
22#include <linux/errno.h>
23#include <linux/fs.h>
24#include <linux/fcntl.h>
25#include <linux/time.h>
26#include <linux/stat.h>
27#include <linux/buffer_head.h> /* for file_fsync() */
28#include <linux/adfs_fs.h>
29
30#include "adfs.h"
31
32struct file_operations adfs_file_operations = {
33 .llseek = generic_file_llseek,
34 .read = generic_file_read,
35 .mmap = generic_file_mmap,
36 .fsync = file_fsync,
37 .write = generic_file_write,
38 .sendfile = generic_file_sendfile,
39};
40
41struct inode_operations adfs_file_inode_operations = {
42 .setattr = adfs_notify_change,
43};
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
new file mode 100644
index 000000000000..a02802a30798
--- /dev/null
+++ b/fs/adfs/inode.c
@@ -0,0 +1,395 @@
1/*
2 * linux/fs/adfs/inode.c
3 *
4 * Copyright (C) 1997-1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/errno.h>
11#include <linux/fs.h>
12#include <linux/adfs_fs.h>
13#include <linux/time.h>
14#include <linux/stat.h>
15#include <linux/string.h>
16#include <linux/mm.h>
17#include <linux/smp_lock.h>
18#include <linux/module.h>
19#include <linux/buffer_head.h>
20
21#include "adfs.h"
22
23/*
24 * Lookup/Create a block at offset 'block' into 'inode'. We currently do
25 * not support creation of new blocks, so we return -EIO for this case.
26 */
27static int
28adfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh,
29 int create)
30{
31 if (block < 0)
32 goto abort_negative;
33
34 if (!create) {
35 if (block >= inode->i_blocks)
36 goto abort_toobig;
37
38 block = __adfs_block_map(inode->i_sb, inode->i_ino, block);
39 if (block)
40 map_bh(bh, inode->i_sb, block);
41 return 0;
42 }
43 /* don't support allocation of blocks yet */
44 return -EIO;
45
46abort_negative:
47 adfs_error(inode->i_sb, "block %d < 0", block);
48 return -EIO;
49
50abort_toobig:
51 return 0;
52}
53
54static int adfs_writepage(struct page *page, struct writeback_control *wbc)
55{
56 return block_write_full_page(page, adfs_get_block, wbc);
57}
58
59static int adfs_readpage(struct file *file, struct page *page)
60{
61 return block_read_full_page(page, adfs_get_block);
62}
63
64static int adfs_prepare_write(struct file *file, struct page *page, unsigned int from, unsigned int to)
65{
66 return cont_prepare_write(page, from, to, adfs_get_block,
67 &ADFS_I(page->mapping->host)->mmu_private);
68}
69
70static sector_t _adfs_bmap(struct address_space *mapping, sector_t block)
71{
72 return generic_block_bmap(mapping, block, adfs_get_block);
73}
74
75static struct address_space_operations adfs_aops = {
76 .readpage = adfs_readpage,
77 .writepage = adfs_writepage,
78 .sync_page = block_sync_page,
79 .prepare_write = adfs_prepare_write,
80 .commit_write = generic_commit_write,
81 .bmap = _adfs_bmap
82};
83
84static inline unsigned int
85adfs_filetype(struct inode *inode)
86{
87 unsigned int type;
88
89 if (ADFS_I(inode)->stamped)
90 type = (ADFS_I(inode)->loadaddr >> 8) & 0xfff;
91 else
92 type = (unsigned int) -1;
93
94 return type;
95}
96
97/*
98 * Convert ADFS attributes and filetype to Linux permission.
99 */
100static umode_t
101adfs_atts2mode(struct super_block *sb, struct inode *inode)
102{
103 unsigned int filetype, attr = ADFS_I(inode)->attr;
104 umode_t mode, rmask;
105 struct adfs_sb_info *asb = ADFS_SB(sb);
106
107 if (attr & ADFS_NDA_DIRECTORY) {
108 mode = S_IRUGO & asb->s_owner_mask;
109 return S_IFDIR | S_IXUGO | mode;
110 }
111
112 filetype = adfs_filetype(inode);
113
114 switch (filetype) {
115 case 0xfc0: /* LinkFS */
116 return S_IFLNK|S_IRWXUGO;
117
118 case 0xfe6: /* UnixExec */
119 rmask = S_IRUGO | S_IXUGO;
120 break;
121
122 default:
123 rmask = S_IRUGO;
124 }
125
126 mode = S_IFREG;
127
128 if (attr & ADFS_NDA_OWNER_READ)
129 mode |= rmask & asb->s_owner_mask;
130
131 if (attr & ADFS_NDA_OWNER_WRITE)
132 mode |= S_IWUGO & asb->s_owner_mask;
133
134 if (attr & ADFS_NDA_PUBLIC_READ)
135 mode |= rmask & asb->s_other_mask;
136
137 if (attr & ADFS_NDA_PUBLIC_WRITE)
138 mode |= S_IWUGO & asb->s_other_mask;
139 return mode;
140}
141
142/*
143 * Convert Linux permission to ADFS attribute. We try to do the reverse
144 * of atts2mode, but there is not a 1:1 translation.
145 */
146static int
147adfs_mode2atts(struct super_block *sb, struct inode *inode)
148{
149 umode_t mode;
150 int attr;
151 struct adfs_sb_info *asb = ADFS_SB(sb);
152
153 /* FIXME: should we be able to alter a link? */
154 if (S_ISLNK(inode->i_mode))
155 return ADFS_I(inode)->attr;
156
157 if (S_ISDIR(inode->i_mode))
158 attr = ADFS_NDA_DIRECTORY;
159 else
160 attr = 0;
161
162 mode = inode->i_mode & asb->s_owner_mask;
163 if (mode & S_IRUGO)
164 attr |= ADFS_NDA_OWNER_READ;
165 if (mode & S_IWUGO)
166 attr |= ADFS_NDA_OWNER_WRITE;
167
168 mode = inode->i_mode & asb->s_other_mask;
169 mode &= ~asb->s_owner_mask;
170 if (mode & S_IRUGO)
171 attr |= ADFS_NDA_PUBLIC_READ;
172 if (mode & S_IWUGO)
173 attr |= ADFS_NDA_PUBLIC_WRITE;
174
175 return attr;
176}
177
178/*
179 * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time
180 * referenced to 1 Jan 1900 (til 2248)
181 */
182static void
183adfs_adfs2unix_time(struct timespec *tv, struct inode *inode)
184{
185 unsigned int high, low;
186
187 if (ADFS_I(inode)->stamped == 0)
188 goto cur_time;
189
190 high = ADFS_I(inode)->loadaddr << 24;
191 low = ADFS_I(inode)->execaddr;
192
193 high |= low >> 8;
194 low &= 255;
195
196 /* Files dated pre 01 Jan 1970 00:00:00. */
197 if (high < 0x336e996a)
198 goto too_early;
199
200 /* Files dated post 18 Jan 2038 03:14:05. */
201 if (high >= 0x656e9969)
202 goto too_late;
203
204 /* discard 2208988800 (0x336e996a00) seconds of time */
205 high -= 0x336e996a;
206
207 /* convert 40-bit centi-seconds to 32-bit seconds */
208 tv->tv_sec = (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
209 tv->tv_nsec = 0;
210 return;
211
212 cur_time:
213 *tv = CURRENT_TIME_SEC;
214 return;
215
216 too_early:
217 tv->tv_sec = tv->tv_nsec = 0;
218 return;
219
220 too_late:
221 tv->tv_sec = 0x7ffffffd;
222 tv->tv_nsec = 0;
223 return;
224}
225
226/*
227 * Convert an Unix time to ADFS time. We only do this if the entry has a
228 * time/date stamp already.
229 */
230static void
231adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
232{
233 unsigned int high, low;
234
235 if (ADFS_I(inode)->stamped) {
236 /* convert 32-bit seconds to 40-bit centi-seconds */
237 low = (secs & 255) * 100;
238 high = (secs / 256) * 100 + (low >> 8) + 0x336e996a;
239
240 ADFS_I(inode)->loadaddr = (high >> 24) |
241 (ADFS_I(inode)->loadaddr & ~0xff);
242 ADFS_I(inode)->execaddr = (low & 255) | (high << 8);
243 }
244}
245
246/*
247 * Fill in the inode information from the object information.
248 *
249 * Note that this is an inode-less filesystem, so we can't use the inode
250 * number to reference the metadata on the media. Instead, we use the
251 * inode number to hold the object ID, which in turn will tell us where
252 * the data is held. We also save the parent object ID, and with these
253 * two, we can locate the metadata.
254 *
255 * This does mean that we rely on an objects parent remaining the same at
256 * all times - we cannot cope with a cross-directory rename (yet).
257 */
258struct inode *
259adfs_iget(struct super_block *sb, struct object_info *obj)
260{
261 struct inode *inode;
262
263 inode = new_inode(sb);
264 if (!inode)
265 goto out;
266
267 inode->i_uid = ADFS_SB(sb)->s_uid;
268 inode->i_gid = ADFS_SB(sb)->s_gid;
269 inode->i_ino = obj->file_id;
270 inode->i_size = obj->size;
271 inode->i_nlink = 2;
272 inode->i_blksize = PAGE_SIZE;
273 inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >>
274 sb->s_blocksize_bits;
275
276 /*
277 * we need to save the parent directory ID so that
278 * write_inode can update the directory information
279 * for this file. This will need special handling
280 * for cross-directory renames.
281 */
282 ADFS_I(inode)->parent_id = obj->parent_id;
283 ADFS_I(inode)->loadaddr = obj->loadaddr;
284 ADFS_I(inode)->execaddr = obj->execaddr;
285 ADFS_I(inode)->attr = obj->attr;
286 ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
287
288 inode->i_mode = adfs_atts2mode(sb, inode);
289 adfs_adfs2unix_time(&inode->i_mtime, inode);
290 inode->i_atime = inode->i_mtime;
291 inode->i_ctime = inode->i_mtime;
292
293 if (S_ISDIR(inode->i_mode)) {
294 inode->i_op = &adfs_dir_inode_operations;
295 inode->i_fop = &adfs_dir_operations;
296 } else if (S_ISREG(inode->i_mode)) {
297 inode->i_op = &adfs_file_inode_operations;
298 inode->i_fop = &adfs_file_operations;
299 inode->i_mapping->a_ops = &adfs_aops;
300 ADFS_I(inode)->mmu_private = inode->i_size;
301 }
302
303 insert_inode_hash(inode);
304
305out:
306 return inode;
307}
308
309/*
310 * Validate and convert a changed access mode/time to their ADFS equivalents.
311 * adfs_write_inode will actually write the information back to the directory
312 * later.
313 */
314int
315adfs_notify_change(struct dentry *dentry, struct iattr *attr)
316{
317 struct inode *inode = dentry->d_inode;
318 struct super_block *sb = inode->i_sb;
319 unsigned int ia_valid = attr->ia_valid;
320 int error;
321
322 lock_kernel();
323
324 error = inode_change_ok(inode, attr);
325
326 /*
327 * we can't change the UID or GID of any file -
328 * we have a global UID/GID in the superblock
329 */
330 if ((ia_valid & ATTR_UID && attr->ia_uid != ADFS_SB(sb)->s_uid) ||
331 (ia_valid & ATTR_GID && attr->ia_gid != ADFS_SB(sb)->s_gid))
332 error = -EPERM;
333
334 if (error)
335 goto out;
336
337 if (ia_valid & ATTR_SIZE)
338 error = vmtruncate(inode, attr->ia_size);
339
340 if (error)
341 goto out;
342
343 if (ia_valid & ATTR_MTIME) {
344 inode->i_mtime = attr->ia_mtime;
345 adfs_unix2adfs_time(inode, attr->ia_mtime.tv_sec);
346 }
347 /*
348 * FIXME: should we make these == to i_mtime since we don't
349 * have the ability to represent them in our filesystem?
350 */
351 if (ia_valid & ATTR_ATIME)
352 inode->i_atime = attr->ia_atime;
353 if (ia_valid & ATTR_CTIME)
354 inode->i_ctime = attr->ia_ctime;
355 if (ia_valid & ATTR_MODE) {
356 ADFS_I(inode)->attr = adfs_mode2atts(sb, inode);
357 inode->i_mode = adfs_atts2mode(sb, inode);
358 }
359
360 /*
361 * FIXME: should we be marking this inode dirty even if
362 * we don't have any metadata to write back?
363 */
364 if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE))
365 mark_inode_dirty(inode);
366out:
367 unlock_kernel();
368 return error;
369}
370
371/*
372 * write an existing inode back to the directory, and therefore the disk.
373 * The adfs-specific inode data has already been updated by
374 * adfs_notify_change()
375 */
376int adfs_write_inode(struct inode *inode, int unused)
377{
378 struct super_block *sb = inode->i_sb;
379 struct object_info obj;
380 int ret;
381
382 lock_kernel();
383 obj.file_id = inode->i_ino;
384 obj.name_len = 0;
385 obj.parent_id = ADFS_I(inode)->parent_id;
386 obj.loadaddr = ADFS_I(inode)->loadaddr;
387 obj.execaddr = ADFS_I(inode)->execaddr;
388 obj.attr = ADFS_I(inode)->attr;
389 obj.size = inode->i_size;
390
391 ret = adfs_dir_update(sb, &obj);
392 unlock_kernel();
393 return ret;
394}
395MODULE_LICENSE("GPL");
diff --git a/fs/adfs/map.c b/fs/adfs/map.c
new file mode 100644
index 000000000000..92ab4fbc2031
--- /dev/null
+++ b/fs/adfs/map.c
@@ -0,0 +1,296 @@
1/*
2 * linux/fs/adfs/map.c
3 *
4 * Copyright (C) 1997-2002 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/errno.h>
11#include <linux/fs.h>
12#include <linux/adfs_fs.h>
13#include <linux/spinlock.h>
14#include <linux/buffer_head.h>
15
16#include <asm/unaligned.h>
17
18#include "adfs.h"
19
20/*
21 * The ADFS map is basically a set of sectors. Each sector is called a
22 * zone which contains a bitstream made up of variable sized fragments.
23 * Each bit refers to a set of bytes in the filesystem, defined by
24 * log2bpmb. This may be larger or smaller than the sector size, but
25 * the overall size it describes will always be a round number of
26 * sectors. A fragment id is always idlen bits long.
27 *
28 * < idlen > < n > <1>
29 * +---------+-------//---------+---+
30 * | frag id | 0000....000000 | 1 |
31 * +---------+-------//---------+---+
32 *
33 * The physical disk space used by a fragment is taken from the start of
34 * the fragment id up to and including the '1' bit - ie, idlen + n + 1
35 * bits.
36 *
37 * A fragment id can be repeated multiple times in the whole map for
38 * large or fragmented files. The first map zone a fragment starts in
39 * is given by fragment id / ids_per_zone - this allows objects to start
40 * from any zone on the disk.
41 *
42 * Free space is described by a linked list of fragments. Each free
43 * fragment describes free space in the same way as the other fragments,
44 * however, the frag id specifies an offset (in map bits) from the end
45 * of this fragment to the start of the next free fragment.
46 *
47 * Objects stored on the disk are allocated object ids (we use these as
48 * our inode numbers.) Object ids contain a fragment id and an optional
49 * offset. This allows a directory fragment to contain small files
50 * associated with that directory.
51 */
52
53/*
54 * For the future...
55 */
56static DEFINE_RWLOCK(adfs_map_lock);
57
58/*
59 * This is fun. We need to load up to 19 bits from the map at an
60 * arbitary bit alignment. (We're limited to 19 bits by F+ version 2).
61 */
62#define GET_FRAG_ID(_map,_start,_idmask) \
63 ({ \
64 unsigned char *_m = _map + (_start >> 3); \
65 u32 _frag = get_unaligned((u32 *)_m); \
66 _frag >>= (_start & 7); \
67 _frag & _idmask; \
68 })
69
70/*
71 * return the map bit offset of the fragment frag_id in the zone dm.
72 * Note that the loop is optimised for best asm code - look at the
73 * output of:
74 * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
75 */
76static int
77lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
78 const unsigned int frag_id, unsigned int *offset)
79{
80 const unsigned int mapsize = dm->dm_endbit;
81 const u32 idmask = (1 << idlen) - 1;
82 unsigned char *map = dm->dm_bh->b_data + 4;
83 unsigned int start = dm->dm_startbit;
84 unsigned int mapptr;
85 u32 frag;
86
87 do {
88 frag = GET_FRAG_ID(map, start, idmask);
89 mapptr = start + idlen;
90
91 /*
92 * find end of fragment
93 */
94 {
95 __le32 *_map = (__le32 *)map;
96 u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
97 while (v == 0) {
98 mapptr = (mapptr & ~31) + 32;
99 if (mapptr >= mapsize)
100 goto error;
101 v = le32_to_cpu(_map[mapptr >> 5]);
102 }
103
104 mapptr += 1 + ffz(~v);
105 }
106
107 if (frag == frag_id)
108 goto found;
109again:
110 start = mapptr;
111 } while (mapptr < mapsize);
112 return -1;
113
114error:
115 printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
116 frag, start, mapptr);
117 return -1;
118
119found:
120 {
121 int length = mapptr - start;
122 if (*offset >= length) {
123 *offset -= length;
124 goto again;
125 }
126 }
127 return start + *offset;
128}
129
130/*
131 * Scan the free space map, for this zone, calculating the total
132 * number of map bits in each free space fragment.
133 *
134 * Note: idmask is limited to 15 bits [3.2]
135 */
136static unsigned int
137scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
138{
139 const unsigned int mapsize = dm->dm_endbit + 32;
140 const unsigned int idlen = asb->s_idlen;
141 const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
142 const u32 idmask = (1 << frag_idlen) - 1;
143 unsigned char *map = dm->dm_bh->b_data;
144 unsigned int start = 8, mapptr;
145 u32 frag;
146 unsigned long total = 0;
147
148 /*
149 * get fragment id
150 */
151 frag = GET_FRAG_ID(map, start, idmask);
152
153 /*
154 * If the freelink is null, then no free fragments
155 * exist in this zone.
156 */
157 if (frag == 0)
158 return 0;
159
160 do {
161 start += frag;
162
163 /*
164 * get fragment id
165 */
166 frag = GET_FRAG_ID(map, start, idmask);
167 mapptr = start + idlen;
168
169 /*
170 * find end of fragment
171 */
172 {
173 __le32 *_map = (__le32 *)map;
174 u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
175 while (v == 0) {
176 mapptr = (mapptr & ~31) + 32;
177 if (mapptr >= mapsize)
178 goto error;
179 v = le32_to_cpu(_map[mapptr >> 5]);
180 }
181
182 mapptr += 1 + ffz(~v);
183 }
184
185 total += mapptr - start;
186 } while (frag >= idlen + 1);
187
188 if (frag != 0)
189 printk(KERN_ERR "adfs: undersized free fragment\n");
190
191 return total;
192error:
193 printk(KERN_ERR "adfs: oversized free fragment\n");
194 return 0;
195}
196
197static int
198scan_map(struct adfs_sb_info *asb, unsigned int zone,
199 const unsigned int frag_id, unsigned int mapoff)
200{
201 const unsigned int idlen = asb->s_idlen;
202 struct adfs_discmap *dm, *dm_end;
203 int result;
204
205 dm = asb->s_map + zone;
206 zone = asb->s_map_size;
207 dm_end = asb->s_map + zone;
208
209 do {
210 result = lookup_zone(dm, idlen, frag_id, &mapoff);
211
212 if (result != -1)
213 goto found;
214
215 dm ++;
216 if (dm == dm_end)
217 dm = asb->s_map;
218 } while (--zone > 0);
219
220 return -1;
221found:
222 result -= dm->dm_startbit;
223 result += dm->dm_startblk;
224
225 return result;
226}
227
228/*
229 * calculate the amount of free blocks in the map.
230 *
231 * n=1
232 * total_free = E(free_in_zone_n)
233 * nzones
234 */
235unsigned int
236adfs_map_free(struct super_block *sb)
237{
238 struct adfs_sb_info *asb = ADFS_SB(sb);
239 struct adfs_discmap *dm;
240 unsigned int total = 0;
241 unsigned int zone;
242
243 dm = asb->s_map;
244 zone = asb->s_map_size;
245
246 do {
247 total += scan_free_map(asb, dm++);
248 } while (--zone > 0);
249
250 return signed_asl(total, asb->s_map2blk);
251}
252
253int
254adfs_map_lookup(struct super_block *sb, unsigned int frag_id,
255 unsigned int offset)
256{
257 struct adfs_sb_info *asb = ADFS_SB(sb);
258 unsigned int zone, mapoff;
259 int result;
260
261 /*
262 * map & root fragment is special - it starts in the center of the
263 * disk. The other fragments start at zone (frag / ids_per_zone)
264 */
265 if (frag_id == ADFS_ROOT_FRAG)
266 zone = asb->s_map_size >> 1;
267 else
268 zone = frag_id / asb->s_ids_per_zone;
269
270 if (zone >= asb->s_map_size)
271 goto bad_fragment;
272
273 /* Convert sector offset to map offset */
274 mapoff = signed_asl(offset, -asb->s_map2blk);
275
276 read_lock(&adfs_map_lock);
277 result = scan_map(asb, zone, frag_id, mapoff);
278 read_unlock(&adfs_map_lock);
279
280 if (result > 0) {
281 unsigned int secoff;
282
283 /* Calculate sector offset into map block */
284 secoff = offset - signed_asl(mapoff, asb->s_map2blk);
285 return secoff + signed_asl(result, asb->s_map2blk);
286 }
287
288 adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
289 frag_id, offset);
290 return 0;
291
292bad_fragment:
293 adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
294 frag_id, zone, asb->s_map_size);
295 return 0;
296}
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
new file mode 100644
index 000000000000..243963228d10
--- /dev/null
+++ b/fs/adfs/super.c
@@ -0,0 +1,508 @@
1/*
2 * linux/fs/adfs/super.c
3 *
4 * Copyright (C) 1997-1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/module.h>
11#include <linux/errno.h>
12#include <linux/fs.h>
13#include <linux/adfs_fs.h>
14#include <linux/slab.h>
15#include <linux/time.h>
16#include <linux/stat.h>
17#include <linux/string.h>
18#include <linux/init.h>
19#include <linux/buffer_head.h>
20#include <linux/vfs.h>
21#include <linux/parser.h>
22#include <linux/bitops.h>
23
24#include <asm/uaccess.h>
25#include <asm/system.h>
26
27#include <stdarg.h>
28
29#include "adfs.h"
30#include "dir_f.h"
31#include "dir_fplus.h"
32
33void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
34{
35 char error_buf[128];
36 va_list args;
37
38 va_start(args, fmt);
39 vsprintf(error_buf, fmt, args);
40 va_end(args);
41
42 printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
43 sb->s_id, function ? ": " : "",
44 function ? function : "", error_buf);
45}
46
47static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
48{
49 int i;
50
51 /* sector size must be 256, 512 or 1024 bytes */
52 if (dr->log2secsize != 8 &&
53 dr->log2secsize != 9 &&
54 dr->log2secsize != 10)
55 return 1;
56
57 /* idlen must be at least log2secsize + 3 */
58 if (dr->idlen < dr->log2secsize + 3)
59 return 1;
60
61 /* we cannot have such a large disc that we
62 * are unable to represent sector offsets in
63 * 32 bits. This works out at 2.0 TB.
64 */
65 if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize)
66 return 1;
67
68 /* idlen must be no greater than 19 v2 [1.0] */
69 if (dr->idlen > 19)
70 return 1;
71
72 /* reserved bytes should be zero */
73 for (i = 0; i < sizeof(dr->unused52); i++)
74 if (dr->unused52[i] != 0)
75 return 1;
76
77 return 0;
78}
79
80static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
81{
82 unsigned int v0, v1, v2, v3;
83 int i;
84
85 v0 = v1 = v2 = v3 = 0;
86 for (i = sb->s_blocksize - 4; i; i -= 4) {
87 v0 += map[i] + (v3 >> 8);
88 v3 &= 0xff;
89 v1 += map[i + 1] + (v0 >> 8);
90 v0 &= 0xff;
91 v2 += map[i + 2] + (v1 >> 8);
92 v1 &= 0xff;
93 v3 += map[i + 3] + (v2 >> 8);
94 v2 &= 0xff;
95 }
96 v0 += v3 >> 8;
97 v1 += map[1] + (v0 >> 8);
98 v2 += map[2] + (v1 >> 8);
99 v3 += map[3] + (v2 >> 8);
100
101 return v0 ^ v1 ^ v2 ^ v3;
102}
103
104static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
105{
106 unsigned char crosscheck = 0, zonecheck = 1;
107 int i;
108
109 for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) {
110 unsigned char *map;
111
112 map = dm[i].dm_bh->b_data;
113
114 if (adfs_calczonecheck(sb, map) != map[0]) {
115 adfs_error(sb, "zone %d fails zonecheck", i);
116 zonecheck = 0;
117 }
118 crosscheck ^= map[3];
119 }
120 if (crosscheck != 0xff)
121 adfs_error(sb, "crosscheck != 0xff");
122 return crosscheck == 0xff && zonecheck;
123}
124
125static void adfs_put_super(struct super_block *sb)
126{
127 int i;
128 struct adfs_sb_info *asb = ADFS_SB(sb);
129
130 for (i = 0; i < asb->s_map_size; i++)
131 brelse(asb->s_map[i].dm_bh);
132 kfree(asb->s_map);
133 kfree(asb);
134 sb->s_fs_info = NULL;
135}
136
137enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err};
138
139static match_table_t tokens = {
140 {Opt_uid, "uid=%u"},
141 {Opt_gid, "gid=%u"},
142 {Opt_ownmask, "ownmask=%o"},
143 {Opt_othmask, "othmask=%o"},
144 {Opt_err, NULL}
145};
146
147static int parse_options(struct super_block *sb, char *options)
148{
149 char *p;
150 struct adfs_sb_info *asb = ADFS_SB(sb);
151 int option;
152
153 if (!options)
154 return 0;
155
156 while ((p = strsep(&options, ",")) != NULL) {
157 substring_t args[MAX_OPT_ARGS];
158 int token;
159 if (!*p)
160 continue;
161
162 token = match_token(p, tokens, args);
163 switch (token) {
164 case Opt_uid:
165 if (match_int(args, &option))
166 return -EINVAL;
167 asb->s_uid = option;
168 break;
169 case Opt_gid:
170 if (match_int(args, &option))
171 return -EINVAL;
172 asb->s_gid = option;
173 break;
174 case Opt_ownmask:
175 if (match_octal(args, &option))
176 return -EINVAL;
177 asb->s_owner_mask = option;
178 break;
179 case Opt_othmask:
180 if (match_octal(args, &option))
181 return -EINVAL;
182 asb->s_other_mask = option;
183 break;
184 default:
185 printk("ADFS-fs: unrecognised mount option \"%s\" "
186 "or missing value\n", p);
187 return -EINVAL;
188 }
189 }
190 return 0;
191}
192
193static int adfs_remount(struct super_block *sb, int *flags, char *data)
194{
195 *flags |= MS_NODIRATIME;
196 return parse_options(sb, data);
197}
198
199static int adfs_statfs(struct super_block *sb, struct kstatfs *buf)
200{
201 struct adfs_sb_info *asb = ADFS_SB(sb);
202
203 buf->f_type = ADFS_SUPER_MAGIC;
204 buf->f_namelen = asb->s_namelen;
205 buf->f_bsize = sb->s_blocksize;
206 buf->f_blocks = asb->s_size;
207 buf->f_files = asb->s_ids_per_zone * asb->s_map_size;
208 buf->f_bavail =
209 buf->f_bfree = adfs_map_free(sb);
210 buf->f_ffree = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks;
211
212 return 0;
213}
214
215static kmem_cache_t *adfs_inode_cachep;
216
217static struct inode *adfs_alloc_inode(struct super_block *sb)
218{
219 struct adfs_inode_info *ei;
220 ei = (struct adfs_inode_info *)kmem_cache_alloc(adfs_inode_cachep, SLAB_KERNEL);
221 if (!ei)
222 return NULL;
223 return &ei->vfs_inode;
224}
225
226static void adfs_destroy_inode(struct inode *inode)
227{
228 kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
229}
230
231static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
232{
233 struct adfs_inode_info *ei = (struct adfs_inode_info *) foo;
234
235 if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
236 SLAB_CTOR_CONSTRUCTOR)
237 inode_init_once(&ei->vfs_inode);
238}
239
240static int init_inodecache(void)
241{
242 adfs_inode_cachep = kmem_cache_create("adfs_inode_cache",
243 sizeof(struct adfs_inode_info),
244 0, SLAB_RECLAIM_ACCOUNT,
245 init_once, NULL);
246 if (adfs_inode_cachep == NULL)
247 return -ENOMEM;
248 return 0;
249}
250
251static void destroy_inodecache(void)
252{
253 if (kmem_cache_destroy(adfs_inode_cachep))
254 printk(KERN_INFO "adfs_inode_cache: not all structures were freed\n");
255}
256
257static struct super_operations adfs_sops = {
258 .alloc_inode = adfs_alloc_inode,
259 .destroy_inode = adfs_destroy_inode,
260 .write_inode = adfs_write_inode,
261 .put_super = adfs_put_super,
262 .statfs = adfs_statfs,
263 .remount_fs = adfs_remount,
264};
265
266static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
267{
268 struct adfs_discmap *dm;
269 unsigned int map_addr, zone_size, nzones;
270 int i, zone;
271 struct adfs_sb_info *asb = ADFS_SB(sb);
272
273 nzones = asb->s_map_size;
274 zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
275 map_addr = (nzones >> 1) * zone_size -
276 ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
277 map_addr = signed_asl(map_addr, asb->s_map2blk);
278
279 asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);
280
281 dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
282 if (dm == NULL) {
283 adfs_error(sb, "not enough memory");
284 return NULL;
285 }
286
287 for (zone = 0; zone < nzones; zone++, map_addr++) {
288 dm[zone].dm_startbit = 0;
289 dm[zone].dm_endbit = zone_size;
290 dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
291 dm[zone].dm_bh = sb_bread(sb, map_addr);
292
293 if (!dm[zone].dm_bh) {
294 adfs_error(sb, "unable to read map");
295 goto error_free;
296 }
297 }
298
299 /* adjust the limits for the first and last map zones */
300 i = zone - 1;
301 dm[0].dm_startblk = 0;
302 dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
303 dm[i].dm_endbit = (le32_to_cpu(dr->disc_size_high) << (32 - dr->log2bpmb)) +
304 (le32_to_cpu(dr->disc_size) >> dr->log2bpmb) +
305 (ADFS_DR_SIZE_BITS - i * zone_size);
306
307 if (adfs_checkmap(sb, dm))
308 return dm;
309
310 adfs_error(sb, NULL, "map corrupted");
311
312error_free:
313 while (--zone >= 0)
314 brelse(dm[zone].dm_bh);
315
316 kfree(dm);
317 return NULL;
318}
319
320static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
321{
322 unsigned long discsize;
323
324 discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
325 discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
326
327 return discsize;
328}
329
330static int adfs_fill_super(struct super_block *sb, void *data, int silent)
331{
332 struct adfs_discrecord *dr;
333 struct buffer_head *bh;
334 struct object_info root_obj;
335 unsigned char *b_data;
336 struct adfs_sb_info *asb;
337 struct inode *root;
338
339 sb->s_flags |= MS_NODIRATIME;
340
341 asb = kmalloc(sizeof(*asb), GFP_KERNEL);
342 if (!asb)
343 return -ENOMEM;
344 sb->s_fs_info = asb;
345 memset(asb, 0, sizeof(*asb));
346
347 /* set default options */
348 asb->s_uid = 0;
349 asb->s_gid = 0;
350 asb->s_owner_mask = S_IRWXU;
351 asb->s_other_mask = S_IRWXG | S_IRWXO;
352
353 if (parse_options(sb, data))
354 goto error;
355
356 sb_set_blocksize(sb, BLOCK_SIZE);
357 if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
358 adfs_error(sb, "unable to read superblock");
359 goto error;
360 }
361
362 b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
363
364 if (adfs_checkbblk(b_data)) {
365 if (!silent)
366 printk("VFS: Can't find an adfs filesystem on dev "
367 "%s.\n", sb->s_id);
368 goto error_free_bh;
369 }
370
371 dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
372
373 /*
374 * Do some sanity checks on the ADFS disc record
375 */
376 if (adfs_checkdiscrecord(dr)) {
377 if (!silent)
378 printk("VPS: Can't find an adfs filesystem on dev "
379 "%s.\n", sb->s_id);
380 goto error_free_bh;
381 }
382
383 brelse(bh);
384 if (sb_set_blocksize(sb, 1 << dr->log2secsize)) {
385 bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
386 if (!bh) {
387 adfs_error(sb, "couldn't read superblock on "
388 "2nd try.");
389 goto error;
390 }
391 b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
392 if (adfs_checkbblk(b_data)) {
393 adfs_error(sb, "disc record mismatch, very weird!");
394 goto error_free_bh;
395 }
396 dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
397 } else {
398 if (!silent)
399 printk(KERN_ERR "VFS: Unsupported blocksize on dev "
400 "%s.\n", sb->s_id);
401 goto error;
402 }
403
404 /*
405 * blocksize on this device should now be set to the ADFS log2secsize
406 */
407
408 sb->s_magic = ADFS_SUPER_MAGIC;
409 asb->s_idlen = dr->idlen;
410 asb->s_map_size = dr->nzones | (dr->nzones_high << 8);
411 asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
412 asb->s_size = adfs_discsize(dr, sb->s_blocksize_bits);
413 asb->s_version = dr->format_version;
414 asb->s_log2sharesize = dr->log2sharesize;
415
416 asb->s_map = adfs_read_map(sb, dr);
417 if (!asb->s_map)
418 goto error_free_bh;
419
420 brelse(bh);
421
422 /*
423 * set up enough so that we can read an inode
424 */
425 sb->s_op = &adfs_sops;
426
427 dr = (struct adfs_discrecord *)(asb->s_map[0].dm_bh->b_data + 4);
428
429 root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
430 root_obj.name_len = 0;
431 root_obj.loadaddr = 0;
432 root_obj.execaddr = 0;
433 root_obj.size = ADFS_NEWDIR_SIZE;
434 root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ |
435 ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
436
437 /*
438 * If this is a F+ disk with variable length directories,
439 * get the root_size from the disc record.
440 */
441 if (asb->s_version) {
442 root_obj.size = le32_to_cpu(dr->root_size);
443 asb->s_dir = &adfs_fplus_dir_ops;
444 asb->s_namelen = ADFS_FPLUS_NAME_LEN;
445 } else {
446 asb->s_dir = &adfs_f_dir_ops;
447 asb->s_namelen = ADFS_F_NAME_LEN;
448 }
449
450 root = adfs_iget(sb, &root_obj);
451 sb->s_root = d_alloc_root(root);
452 if (!sb->s_root) {
453 int i;
454 iput(root);
455 for (i = 0; i < asb->s_map_size; i++)
456 brelse(asb->s_map[i].dm_bh);
457 kfree(asb->s_map);
458 adfs_error(sb, "get root inode failed\n");
459 goto error;
460 } else
461 sb->s_root->d_op = &adfs_dentry_operations;
462 return 0;
463
464error_free_bh:
465 brelse(bh);
466error:
467 sb->s_fs_info = NULL;
468 kfree(asb);
469 return -EINVAL;
470}
471
472static struct super_block *adfs_get_sb(struct file_system_type *fs_type,
473 int flags, const char *dev_name, void *data)
474{
475 return get_sb_bdev(fs_type, flags, dev_name, data, adfs_fill_super);
476}
477
478static struct file_system_type adfs_fs_type = {
479 .owner = THIS_MODULE,
480 .name = "adfs",
481 .get_sb = adfs_get_sb,
482 .kill_sb = kill_block_super,
483 .fs_flags = FS_REQUIRES_DEV,
484};
485
486static int __init init_adfs_fs(void)
487{
488 int err = init_inodecache();
489 if (err)
490 goto out1;
491 err = register_filesystem(&adfs_fs_type);
492 if (err)
493 goto out;
494 return 0;
495out:
496 destroy_inodecache();
497out1:
498 return err;
499}
500
501static void __exit exit_adfs_fs(void)
502{
503 unregister_filesystem(&adfs_fs_type);
504 destroy_inodecache();
505}
506
507module_init(init_adfs_fs)
508module_exit(exit_adfs_fs)