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
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>
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. */
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***";
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.
54 affs_insert_hash(unsigned long next
, struct buffer_head
*file
, struct inode
*inode
)
56 struct buffer_head
*bh
;
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
);
70 if (!(bh
= affs_bread(inode
->i_dev
,next
,AFFS_I2BSIZE(inode
))))
72 next
= be32_to_cpu(((s32
*)bh
->b_data
)[offset
]);
73 if (!next
|| next
> ino
)
75 offset
= AFFS_I2BSIZE(inode
) / 4 - 4;
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
,1);
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
94 affs_remove_hash(struct buffer_head
*dbh
, struct inode
*inode
)
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);
112 lock_super(inode
->i_sb
);
114 if (!(bh
= affs_bread(inode
->i_dev
,key
,AFFS_I2BSIZE(inode
)))) {
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
);
129 key
= be32_to_cpu(((s32
*)bh
->b_data
)[offset
]);
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
,1);
139 offset
= AFFS_I2BSIZE(inode
) / 4 - 4;
141 unlock_super(inode
->i_sb
);
146 /* Remove header from link chain */
149 affs_remove_link(struct buffer_head
*dbh
, struct inode
*inode
)
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
);
162 pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey
,key
);
164 lock_super(inode
->i_sb
);
166 if (!(bh
= affs_bread(inode
->i_dev
,key
,AFFS_I2BSIZE(inode
)))) {
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
);
176 key
= be32_to_cpu(FILE_END(bh
->b_data
,inode
)->link_chain
);
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
,1);
188 unlock_super(inode
->i_sb
);
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
;
207 unsigned long link_ino
;
208 unsigned long orig_ino
;
209 unsigned int dir_ino
;
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
));
221 dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
223 mark_inode_dirty(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
)))
231 if ((error
= affs_remove_hash(bh
,inode
)))
233 affs_free_block(inode
->i_sb
,be32_to_cpu(DIR_FRONT(bh
)->own_key
));
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
)))
243 if (!(link_bh
= affs_bread(inode
->i_dev
,link_ino
,AFFS_I2BSIZE(inode
))))
245 if ((error
= affs_remove_hash(link_bh
,inode
))) {
246 affs_brelse(link_bh
);
249 /* Fix link chain. */
250 if ((error
= affs_remove_link(link_bh
,inode
))) {
251 affs_brelse(link_bh
);
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
);
263 affs_fix_checksum(AFFS_I2BSIZE(inode
),bh
->b_data
,5);
264 mark_buffer_dirty(bh
,1);
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
)))
270 dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
272 mark_inode_dirty(dir
);
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
)))
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
)
299 *ptype
= be32_to_cpu(((s32
*)data
)[0]);
301 *stype
= be32_to_cpu(((s32
*)data
)[bsize
- 1]);
306 sum
+= be32_to_cpu(*p
++);
311 * Calculate the checksum of a disk block and store it
312 * at the indicated position.
316 affs_fix_checksum(int bsize
, void *data
, int cspos
)
321 cs
= affs_checksum_block(bsize
,data
,NULL
,NULL
);
322 ocs
= be32_to_cpu(((u32
*)data
)[cspos
]);
324 ((u32
*)data
)[cspos
] = be32_to_cpu(ocs
);
328 secs_to_datestamp(time_t secs
, struct DateStamp
*ds
)
333 secs
-= sys_tz
.tz_minuteswest
* 60 + ((8 * 365 + 2) * 24 * 60 * 60);
337 secs
-= days
* 86400;
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
)
351 if (AFFS_UMAYWRITE(prot
))
353 if (AFFS_UMAYREAD(prot
))
355 if (AFFS_UMAYEXECUTE(prot
))
357 if (AFFS_GMAYWRITE(prot
))
359 if (AFFS_GMAYREAD(prot
))
361 if (AFFS_GMAYEXECUTE(prot
))
363 if (AFFS_OMAYWRITE(prot
))
365 if (AFFS_OMAYREAD(prot
))
367 if (AFFS_OMAYEXECUTE(prot
))
374 mode_to_prot(int mode
)
383 prot
|= FIBF_WRITE
| FIBF_DELETE
;
385 prot
|= FIBF_GRP_READ
;
387 prot
|= FIBF_GRP_WRITE
;
389 prot
|= FIBF_OTR_READ
;
391 prot
|= FIBF_OTR_WRITE
;
397 affs_error(struct super_block
*sb
, const char *function
, const char *fmt
, ...)
402 vsprintf(ErrorBuffer
,fmt
,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 */
414 affs_warning(struct super_block
*sb
, const char *function
, const char *fmt
, ...)
419 vsprintf(ErrorBuffer
,fmt
,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
)
434 #ifdef AFFS_NO_TRUNCATE
435 return -ENAMETOOLONG
;
440 for (i
= 0; i
< len
; i
++) {
441 if (name
[i
] < ' ' || name
[i
] == ':'
442 || (name
[i
] > 0x7e && name
[i
] < 0xa0))
449 /* This function copies name to bstr, with at most 30
450 * characters length. The bstr will be prepended by
452 * NOTE: The name will must be already checked by
457 affs_copy_name(unsigned char *bstr
, const unsigned char *name
)
461 for (len
= 0; len
< 30; len
++) {
462 bstr
[len
+ 1] = name
[len
];
463 if (name
[len
] == '\0')