- pre5:
[davej-history.git] / fs / affs / amigaffs.c
blob5c4ec8e6b1ce3e2fae1ee1c745c122bf749b2568
1 /*
2 * linux/fs/affs/amigaffs.c
4 * (c) 1996 Hans-Joachim Widmaier - Rewritten
6 * (C) 1993 Ray Burr - Amiga FFS filesystem.
8 * Please send bug reports to: hjw@zvw.de
9 */
11 #define DEBUG 0
12 #include <stdarg.h>
13 #include <linux/stat.h>
14 #include <linux/sched.h>
15 #include <linux/affs_fs.h>
16 #include <linux/string.h>
17 #include <linux/locks.h>
18 #include <linux/mm.h>
19 #include <linux/amigaffs.h>
21 extern struct timezone sys_tz;
23 static char ErrorBuffer[256];
26 * Functions for accessing Amiga-FFS structures.
29 /* Set *NAME to point to the file name in a file header block in memory
30 pointed to by FH_DATA. The length of the name is returned. */
32 int
33 affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
35 struct file_end *file_end;
37 file_end = GET_END_PTR(struct file_end, fh_data, bsize);
38 if (file_end->file_name[0] == 0
39 || file_end->file_name[0] > 30) {
40 printk(KERN_WARNING "AFFS: bad filename (length=%d chars)\n",
41 file_end->file_name[0]);
42 *name = "***BAD_FILE***";
43 return 14;
45 *name = (unsigned char *)&file_end->file_name[1];
46 return file_end->file_name[0];
49 /* Insert a header block (file) into the directory (next).
50 * This routine assumes that the caller has the superblock locked.
53 int
54 affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
56 struct buffer_head *bh;
57 s32 ino;
58 int offset;
60 offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,
61 FILE_END(file->b_data,inode)->file_name[0],
62 AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
63 ino = be32_to_cpu(((struct dir_front *)file->b_data)->own_key);
65 pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
67 FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);
69 while (1) {
70 if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))
71 return -EIO;
72 next = be32_to_cpu(((s32 *)bh->b_data)[offset]);
73 if (!next || next > ino)
74 break;
75 offset = AFFS_I2BSIZE(inode) / 4 - 4;
76 affs_brelse(bh);
79 DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next);
80 ((s32 *)bh->b_data)[offset] = cpu_to_be32(ino);
81 affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
82 mark_buffer_dirty(bh);
83 affs_brelse(bh);
85 return 0;
87 /* Remove a header block from its hash table (directory).
88 * 'inode' may be any inode on the partition, it's only
89 * used for calculating the block size and superblock
90 * reference.
93 int
94 affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
96 s32 ownkey;
97 s32 key;
98 s32 ptype;
99 s32 stype;
100 int offset;
101 int retval;
102 struct buffer_head *bh;
104 ownkey = be32_to_cpu(((struct dir_front *)dbh->b_data)->own_key);
105 key = be32_to_cpu(FILE_END(dbh->b_data,inode)->parent);
106 offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,
107 FILE_END(dbh->b_data,inode)->file_name[0],
108 AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
109 pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);
110 retval = -ENOENT;
112 lock_super(inode->i_sb);
113 while (key) {
114 if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
115 retval = -EIO;
116 break;
118 if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)
119 || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
120 stype != ST_LINKFILE && stype != ST_LINKDIR &&
121 stype != ST_ROOT && stype != ST_SOFTLINK)) {
122 affs_error(inode->i_sb,"affs_remove_hash",
123 "Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",
124 key,ptype,stype,ownkey);
125 affs_brelse(bh);
126 retval = -EINVAL;
127 break;
129 key = be32_to_cpu(((s32 *)bh->b_data)[offset]);
130 if (ownkey == key) {
131 ((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;
132 affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
133 mark_buffer_dirty(bh);
134 affs_brelse(bh);
135 retval = 0;
136 break;
138 affs_brelse(bh);
139 offset = AFFS_I2BSIZE(inode) / 4 - 4;
141 unlock_super(inode->i_sb);
143 return retval;
146 /* Remove header from link chain */
149 affs_remove_link(struct buffer_head *dbh, struct inode *inode)
151 int retval;
152 s32 key;
153 s32 ownkey;
154 s32 ptype;
155 s32 stype;
156 struct buffer_head *bh;
158 ownkey = be32_to_cpu((DIR_FRONT(dbh)->own_key));
159 key = be32_to_cpu(FILE_END(dbh->b_data,inode)->original);
160 retval = -ENOENT;
162 pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
164 lock_super(inode->i_sb);
165 while (key) {
166 if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
167 retval = -EIO;
168 break;
170 if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {
171 affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);
172 affs_brelse(bh);
173 retval = -EINVAL;
174 break;
176 key = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
177 if (ownkey == key) {
178 FILE_END(bh->b_data,inode)->link_chain =
179 FILE_END(dbh->b_data,inode)->link_chain;
180 affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
181 mark_buffer_dirty(bh);
182 affs_brelse(bh);
183 retval = 0;
184 break;
186 affs_brelse(bh);
188 unlock_super(inode->i_sb);
190 return retval;
193 /* Remove a filesystem object. If the object to be removed has
194 * links to it, one of the links must be changed to inherit
195 * the file or directory. As above, any inode will do.
196 * The buffer will not be freed. If the header is a link, the
197 * block will be marked as free.
198 * This function returns a negative error number in case of
199 * an error, else 0 if the inode is to be deleted or 1 if not.
203 affs_remove_header(struct buffer_head *bh, struct inode *inode)
205 struct buffer_head *link_bh;
206 struct inode *dir;
207 unsigned long link_ino;
208 unsigned long orig_ino;
209 unsigned int dir_ino;
210 int error;
212 pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));
214 /* Mark directory as changed. We do this before anything else,
215 * as it must be done anyway and doesn't hurt even if an
216 * error occurs later.
218 dir = iget(inode->i_sb,be32_to_cpu(FILE_END(bh->b_data,inode)->parent));
219 if (!dir)
220 return -EIO;
221 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
222 dir->i_version++;
223 mark_inode_dirty(dir);
224 iput(dir);
226 orig_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->original);
227 if (orig_ino) { /* This is just a link. Nothing much to do. */
228 pr_debug("AFFS: Removing link.\n");
229 if ((error = affs_remove_link(bh,inode)))
230 return error;
231 if ((error = affs_remove_hash(bh,inode)))
232 return error;
233 affs_free_block(inode->i_sb,be32_to_cpu(DIR_FRONT(bh)->own_key));
234 return 1;
237 link_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
238 if (link_ino) { /* This is the complicated case. Yuck. */
239 pr_debug("AFFS: Removing original with links to it.\n");
240 /* Unlink the object and its first link from their directories. */
241 if ((error = affs_remove_hash(bh,inode)))
242 return error;
243 if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))
244 return -EIO;
245 if ((error = affs_remove_hash(link_bh,inode))) {
246 affs_brelse(link_bh);
247 return error;
249 /* Fix link chain. */
250 if ((error = affs_remove_link(link_bh,inode))) {
251 affs_brelse(link_bh);
252 return error;
254 /* Rename link to object. */
255 memcpy(FILE_END(bh->b_data,inode)->file_name,
256 FILE_END(link_bh->b_data,inode)->file_name,32);
257 /* Insert object into dir the link was in. */
258 dir_ino = be32_to_cpu(FILE_END(link_bh->b_data,inode)->parent);
259 if ((error = affs_insert_hash(dir_ino,bh,inode))) {
260 affs_brelse(link_bh);
261 return error;
263 affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
264 mark_buffer_dirty(bh);
265 affs_brelse(link_bh);
266 affs_free_block(inode->i_sb,link_ino);
267 /* Mark the link's parent dir as changed, too. */
268 if (!(dir = iget(inode->i_sb,dir_ino)))
269 return -EIO;
270 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
271 dir->i_version++;
272 mark_inode_dirty(dir);
273 iput(dir);
274 return 1;
276 /* Plain file/dir. This is the simplest case. */
277 pr_debug("AFFS: Removing plain file/dir.\n");
278 if ((error = affs_remove_hash(bh,inode)))
279 return error;
280 return 0;
284 /* Checksum a block, do various consistency checks and optionally return
285 the blocks type number. DATA points to the block. If their pointers
286 are non-null, *PTYPE and *STYPE are set to the primary and secondary
287 block types respectively, *HASHSIZE is set to the size of the hashtable
288 (which lets us calculate the block size).
289 Returns non-zero if the block is not consistent. */
292 affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype)
294 u32 sum;
295 u32 *p;
297 bsize /= 4;
298 if (ptype)
299 *ptype = be32_to_cpu(((s32 *)data)[0]);
300 if (stype)
301 *stype = be32_to_cpu(((s32 *)data)[bsize - 1]);
303 sum = 0;
304 p = data;
305 while (bsize--)
306 sum += be32_to_cpu(*p++);
307 return sum;
311 * Calculate the checksum of a disk block and store it
312 * at the indicated position.
315 void
316 affs_fix_checksum(int bsize, void *data, int cspos)
318 u32 ocs;
319 u32 cs;
321 cs = affs_checksum_block(bsize,data,NULL,NULL);
322 ocs = be32_to_cpu(((u32 *)data)[cspos]);
323 ocs -= cs;
324 ((u32 *)data)[cspos] = be32_to_cpu(ocs);
327 void
328 secs_to_datestamp(time_t secs, struct DateStamp *ds)
330 u32 days;
331 u32 minute;
333 secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
334 if (secs < 0)
335 secs = 0;
336 days = secs / 86400;
337 secs -= days * 86400;
338 minute = secs / 60;
339 secs -= minute * 60;
341 ds->ds_Days = be32_to_cpu(days);
342 ds->ds_Minute = be32_to_cpu(minute);
343 ds->ds_Tick = be32_to_cpu(secs * 50);
347 prot_to_mode(u32 prot)
349 int mode = 0;
351 if (AFFS_UMAYWRITE(prot))
352 mode |= S_IWUSR;
353 if (AFFS_UMAYREAD(prot))
354 mode |= S_IRUSR;
355 if (AFFS_UMAYEXECUTE(prot))
356 mode |= S_IXUSR;
357 if (AFFS_GMAYWRITE(prot))
358 mode |= S_IWGRP;
359 if (AFFS_GMAYREAD(prot))
360 mode |= S_IRGRP;
361 if (AFFS_GMAYEXECUTE(prot))
362 mode |= S_IXGRP;
363 if (AFFS_OMAYWRITE(prot))
364 mode |= S_IWOTH;
365 if (AFFS_OMAYREAD(prot))
366 mode |= S_IROTH;
367 if (AFFS_OMAYEXECUTE(prot))
368 mode |= S_IXOTH;
370 return mode;
374 mode_to_prot(int mode)
376 u32 prot = 0;
378 if (mode & S_IXUSR)
379 prot |= FIBF_SCRIPT;
380 if (mode & S_IRUSR)
381 prot |= FIBF_READ;
382 if (mode & S_IWUSR)
383 prot |= FIBF_WRITE | FIBF_DELETE;
384 if (mode & S_IRGRP)
385 prot |= FIBF_GRP_READ;
386 if (mode & S_IWGRP)
387 prot |= FIBF_GRP_WRITE;
388 if (mode & S_IROTH)
389 prot |= FIBF_OTR_READ;
390 if (mode & S_IWOTH)
391 prot |= FIBF_OTR_WRITE;
393 return prot;
396 void
397 affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
399 va_list args;
401 va_start(args,fmt);
402 vsprintf(ErrorBuffer,fmt,args);
403 va_end(args);
405 printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),
406 function,ErrorBuffer);
407 if (!(sb->s_flags & MS_RDONLY))
408 printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
409 sb->s_flags |= MS_RDONLY;
410 sb->u.affs_sb.s_flags |= SF_READONLY; /* Don't allow to remount rw */
413 void
414 affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
416 va_list args;
418 va_start(args,fmt);
419 vsprintf(ErrorBuffer,fmt,args);
420 va_end(args);
422 printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
423 function,ErrorBuffer);
426 /* Check if the name is valid for a affs object. */
429 affs_check_name(const unsigned char *name, int len)
431 int i;
433 if (len > 30)
434 #ifdef AFFS_NO_TRUNCATE
435 return -ENAMETOOLONG;
436 #else
437 len = 30;
438 #endif
440 for (i = 0; i < len; i++) {
441 if (name[i] < ' ' || name[i] == ':'
442 || (name[i] > 0x7e && name[i] < 0xa0))
443 return -EINVAL;
446 return 0;
449 /* This function copies name to bstr, with at most 30
450 * characters length. The bstr will be prepended by
451 * a length byte.
452 * NOTE: The name will must be already checked by
453 * affs_check_name()!
457 affs_copy_name(unsigned char *bstr, const unsigned char *name)
459 int len;
461 for (len = 0; len < 30; len++) {
462 bstr[len + 1] = name[len];
463 if (name[len] == '\0')
464 break;
466 bstr[0] = len;
467 return len;