Import changeset
[linux-flexiantxendom0-3.2.10.git] / fs / affs / amigaffs.c
1 /*
2  *  linux/fs/affs/amigaffs.c
3  *
4  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
5  *
6  *  (C) 1993  Ray Burr - Amiga FFS filesystem.
7  *
8  *  Please send bug reports to: hjw@zvw.de
9  */
10
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>
20
21 extern struct timezone sys_tz;
22
23 static char ErrorBuffer[256];
24
25 /*
26  * Functions for accessing Amiga-FFS structures.
27  */
28
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. */
31
32 int
33 affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
34 {
35         struct file_end *file_end;
36
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;
44         }
45         *name = (unsigned char *)&file_end->file_name[1];
46         return file_end->file_name[0];
47 }
48
49 /* Insert a header block (file) into the directory (next).
50  * This routine assumes that the caller has the superblock locked.
51  */
52
53 int
54 affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
55 {
56         struct buffer_head      *bh;
57         s32                      ino;
58         int                      offset;
59
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);
64
65         pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
66
67         FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);
68
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);
77         }
78
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);
84
85         return 0;
86 }
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.
91  */
92
93 int
94 affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
95 {
96         s32                      ownkey;
97         s32                      key;
98         s32                      ptype;
99         s32                      stype;
100         int                      offset;
101         int                      retval;
102         struct buffer_head      *bh;
103
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;
111
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;
117                 }
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;
128                 }
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;
137                 }
138                 affs_brelse(bh);
139                 offset = AFFS_I2BSIZE(inode) / 4 - 4;
140         }
141         unlock_super(inode->i_sb);
142
143         return retval;
144 }
145
146 /* Remove header from link chain */
147
148 int
149 affs_remove_link(struct buffer_head *dbh, struct inode *inode)
150 {
151         int                      retval;
152         s32                      key;
153         s32                      ownkey;
154         s32                      ptype;
155         s32                      stype;
156         struct buffer_head      *bh;
157
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;
161
162         pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
163
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;
169                 }
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;
175                 }
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;
185                 }
186                 affs_brelse(bh);
187         }
188         unlock_super(inode->i_sb);
189
190         return retval;
191 }
192
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.
200  */
201
202 int
203 affs_remove_header(struct buffer_head *bh, struct inode *inode)
204 {
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;
211
212         pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));
213
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.
217          */
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);
225
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;
235         }
236         
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;
248                 }
249                 /* Fix link chain. */
250                 if ((error = affs_remove_link(link_bh,inode))) {
251                         affs_brelse(link_bh);
252                         return error;
253                 }
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;
262                 }
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;
275         }
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;
281 }
282
283
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. */
290
291 u32
292 affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype)
293 {
294         u32      sum;
295         u32     *p;
296
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]);
302
303         sum    = 0;
304         p      = data;
305         while (bsize--)
306                 sum += be32_to_cpu(*p++);
307         return sum;
308 }
309
310 /*
311  * Calculate the checksum of a disk block and store it
312  * at the indicated position.
313  */
314
315 void
316 affs_fix_checksum(int bsize, void *data, int cspos)
317 {
318         u32      ocs;
319         u32      cs;
320
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);
325 }
326
327 void
328 secs_to_datestamp(time_t secs, struct DateStamp *ds)
329 {
330         u32      days;
331         u32      minute;
332
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;
340
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);
344 }
345
346 int
347 prot_to_mode(u32 prot)
348 {
349         int      mode = 0;
350
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;
369         
370         return mode;
371 }
372
373 u32
374 mode_to_prot(int mode)
375 {
376         u32      prot = 0;
377
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;
392         
393         return prot;
394 }
395
396 void
397 affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
398 {
399         va_list  args;
400
401         va_start(args,fmt);
402         vsprintf(ErrorBuffer,fmt,args);
403         va_end(args);
404
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 */
411 }
412
413 void
414 affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
415 {
416         va_list  args;
417
418         va_start(args,fmt);
419         vsprintf(ErrorBuffer,fmt,args);
420         va_end(args);
421
422         printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
423                 function,ErrorBuffer);
424 }
425
426 /* Check if the name is valid for a affs object. */
427
428 int
429 affs_check_name(const unsigned char *name, int len)
430 {
431         int      i;
432
433         if (len > 30)
434 #ifdef AFFS_NO_TRUNCATE
435                 return -ENAMETOOLONG;
436 #else
437                 len = 30;
438 #endif
439
440         for (i = 0; i < len; i++) {
441                 if (name[i] < ' ' || name[i] == ':'
442                     || (name[i] > 0x7e && name[i] < 0xa0))
443                         return -EINVAL;
444         }
445
446         return 0;
447 }
448
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()!
454  */
455
456 int
457 affs_copy_name(unsigned char *bstr, const unsigned char *name)
458 {
459         int      len;
460
461         for (len = 0; len < 30; len++) {
462                 bstr[len + 1] = name[len];
463                 if (name[len] == '\0')
464                         break;
465         }
466         bstr[0] = len;
467         return len;
468 }