Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/affs/amigaffs.c
15109 views
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: [email protected]
9
*/
10
11
#include "affs.h"
12
13
extern struct timezone sys_tz;
14
15
static char ErrorBuffer[256];
16
17
/*
18
* Functions for accessing Amiga-FFS structures.
19
*/
20
21
22
/* Insert a header block bh into the directory dir
23
* caller must hold AFFS_DIR->i_hash_lock!
24
*/
25
26
int
27
affs_insert_hash(struct inode *dir, struct buffer_head *bh)
28
{
29
struct super_block *sb = dir->i_sb;
30
struct buffer_head *dir_bh;
31
u32 ino, hash_ino;
32
int offset;
33
34
ino = bh->b_blocknr;
35
offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
36
37
pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino);
38
39
dir_bh = affs_bread(sb, dir->i_ino);
40
if (!dir_bh)
41
return -EIO;
42
43
hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
44
while (hash_ino) {
45
affs_brelse(dir_bh);
46
dir_bh = affs_bread(sb, hash_ino);
47
if (!dir_bh)
48
return -EIO;
49
hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
50
}
51
AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
52
AFFS_TAIL(sb, bh)->hash_chain = 0;
53
affs_fix_checksum(sb, bh);
54
55
if (dir->i_ino == dir_bh->b_blocknr)
56
AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
57
else
58
AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
59
60
affs_adjust_checksum(dir_bh, ino);
61
mark_buffer_dirty_inode(dir_bh, dir);
62
affs_brelse(dir_bh);
63
64
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
65
dir->i_version++;
66
mark_inode_dirty(dir);
67
68
return 0;
69
}
70
71
/* Remove a header block from its directory.
72
* caller must hold AFFS_DIR->i_hash_lock!
73
*/
74
75
int
76
affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
77
{
78
struct super_block *sb;
79
struct buffer_head *bh;
80
u32 rem_ino, hash_ino;
81
__be32 ino;
82
int offset, retval;
83
84
sb = dir->i_sb;
85
rem_ino = rem_bh->b_blocknr;
86
offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
87
pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset);
88
89
bh = affs_bread(sb, dir->i_ino);
90
if (!bh)
91
return -EIO;
92
93
retval = -ENOENT;
94
hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]);
95
while (hash_ino) {
96
if (hash_ino == rem_ino) {
97
ino = AFFS_TAIL(sb, rem_bh)->hash_chain;
98
if (dir->i_ino == bh->b_blocknr)
99
AFFS_HEAD(bh)->table[offset] = ino;
100
else
101
AFFS_TAIL(sb, bh)->hash_chain = ino;
102
affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
103
mark_buffer_dirty_inode(bh, dir);
104
AFFS_TAIL(sb, rem_bh)->parent = 0;
105
retval = 0;
106
break;
107
}
108
affs_brelse(bh);
109
bh = affs_bread(sb, hash_ino);
110
if (!bh)
111
return -EIO;
112
hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
113
}
114
115
affs_brelse(bh);
116
117
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
118
dir->i_version++;
119
mark_inode_dirty(dir);
120
121
return retval;
122
}
123
124
static void
125
affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
126
{
127
struct inode *inode = dentry->d_inode;
128
void *data = dentry->d_fsdata;
129
struct list_head *head, *next;
130
131
spin_lock(&inode->i_lock);
132
head = &inode->i_dentry;
133
next = head->next;
134
while (next != head) {
135
dentry = list_entry(next, struct dentry, d_alias);
136
if (entry_ino == (u32)(long)dentry->d_fsdata) {
137
dentry->d_fsdata = data;
138
break;
139
}
140
next = next->next;
141
}
142
spin_unlock(&inode->i_lock);
143
}
144
145
146
/* Remove header from link chain */
147
148
static int
149
affs_remove_link(struct dentry *dentry)
150
{
151
struct inode *dir, *inode = dentry->d_inode;
152
struct super_block *sb = inode->i_sb;
153
struct buffer_head *bh = NULL, *link_bh = NULL;
154
u32 link_ino, ino;
155
int retval;
156
157
pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino);
158
retval = -EIO;
159
bh = affs_bread(sb, inode->i_ino);
160
if (!bh)
161
goto done;
162
163
link_ino = (u32)(long)dentry->d_fsdata;
164
if (inode->i_ino == link_ino) {
165
/* we can't remove the head of the link, as its blocknr is still used as ino,
166
* so we remove the block of the first link instead.
167
*/
168
link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain);
169
link_bh = affs_bread(sb, link_ino);
170
if (!link_bh)
171
goto done;
172
173
dir = affs_iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent));
174
if (IS_ERR(dir)) {
175
retval = PTR_ERR(dir);
176
goto done;
177
}
178
179
affs_lock_dir(dir);
180
affs_fix_dcache(dentry, link_ino);
181
retval = affs_remove_hash(dir, link_bh);
182
if (retval) {
183
affs_unlock_dir(dir);
184
goto done;
185
}
186
mark_buffer_dirty_inode(link_bh, inode);
187
188
memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
189
retval = affs_insert_hash(dir, bh);
190
if (retval) {
191
affs_unlock_dir(dir);
192
goto done;
193
}
194
mark_buffer_dirty_inode(bh, inode);
195
196
affs_unlock_dir(dir);
197
iput(dir);
198
} else {
199
link_bh = affs_bread(sb, link_ino);
200
if (!link_bh)
201
goto done;
202
}
203
204
while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain)) != 0) {
205
if (ino == link_ino) {
206
__be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain;
207
AFFS_TAIL(sb, bh)->link_chain = ino2;
208
affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino);
209
mark_buffer_dirty_inode(bh, inode);
210
retval = 0;
211
/* Fix the link count, if bh is a normal header block without links */
212
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
213
case ST_LINKDIR:
214
case ST_LINKFILE:
215
break;
216
default:
217
if (!AFFS_TAIL(sb, bh)->link_chain)
218
inode->i_nlink = 1;
219
}
220
affs_free_block(sb, link_ino);
221
goto done;
222
}
223
affs_brelse(bh);
224
bh = affs_bread(sb, ino);
225
if (!bh)
226
goto done;
227
}
228
retval = -ENOENT;
229
done:
230
affs_brelse(link_bh);
231
affs_brelse(bh);
232
return retval;
233
}
234
235
236
static int
237
affs_empty_dir(struct inode *inode)
238
{
239
struct super_block *sb = inode->i_sb;
240
struct buffer_head *bh;
241
int retval, size;
242
243
retval = -EIO;
244
bh = affs_bread(sb, inode->i_ino);
245
if (!bh)
246
goto done;
247
248
retval = -ENOTEMPTY;
249
for (size = AFFS_SB(sb)->s_hashsize - 1; size >= 0; size--)
250
if (AFFS_HEAD(bh)->table[size])
251
goto not_empty;
252
retval = 0;
253
not_empty:
254
affs_brelse(bh);
255
done:
256
return retval;
257
}
258
259
260
/* Remove a filesystem object. If the object to be removed has
261
* links to it, one of the links must be changed to inherit
262
* the file or directory. As above, any inode will do.
263
* The buffer will not be freed. If the header is a link, the
264
* block will be marked as free.
265
* This function returns a negative error number in case of
266
* an error, else 0 if the inode is to be deleted or 1 if not.
267
*/
268
269
int
270
affs_remove_header(struct dentry *dentry)
271
{
272
struct super_block *sb;
273
struct inode *inode, *dir;
274
struct buffer_head *bh = NULL;
275
int retval;
276
277
dir = dentry->d_parent->d_inode;
278
sb = dir->i_sb;
279
280
retval = -ENOENT;
281
inode = dentry->d_inode;
282
if (!inode)
283
goto done;
284
285
pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino);
286
retval = -EIO;
287
bh = affs_bread(sb, (u32)(long)dentry->d_fsdata);
288
if (!bh)
289
goto done;
290
291
affs_lock_link(inode);
292
affs_lock_dir(dir);
293
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
294
case ST_USERDIR:
295
/* if we ever want to support links to dirs
296
* i_hash_lock of the inode must only be
297
* taken after some checks
298
*/
299
affs_lock_dir(inode);
300
retval = affs_empty_dir(inode);
301
affs_unlock_dir(inode);
302
if (retval)
303
goto done_unlock;
304
break;
305
default:
306
break;
307
}
308
309
retval = affs_remove_hash(dir, bh);
310
if (retval)
311
goto done_unlock;
312
mark_buffer_dirty_inode(bh, inode);
313
314
affs_unlock_dir(dir);
315
316
if (inode->i_nlink > 1)
317
retval = affs_remove_link(dentry);
318
else
319
inode->i_nlink = 0;
320
affs_unlock_link(inode);
321
inode->i_ctime = CURRENT_TIME_SEC;
322
mark_inode_dirty(inode);
323
324
done:
325
affs_brelse(bh);
326
return retval;
327
328
done_unlock:
329
affs_unlock_dir(dir);
330
affs_unlock_link(inode);
331
goto done;
332
}
333
334
/* Checksum a block, do various consistency checks and optionally return
335
the blocks type number. DATA points to the block. If their pointers
336
are non-null, *PTYPE and *STYPE are set to the primary and secondary
337
block types respectively, *HASHSIZE is set to the size of the hashtable
338
(which lets us calculate the block size).
339
Returns non-zero if the block is not consistent. */
340
341
u32
342
affs_checksum_block(struct super_block *sb, struct buffer_head *bh)
343
{
344
__be32 *ptr = (__be32 *)bh->b_data;
345
u32 sum;
346
int bsize;
347
348
sum = 0;
349
for (bsize = sb->s_blocksize / sizeof(__be32); bsize > 0; bsize--)
350
sum += be32_to_cpu(*ptr++);
351
return sum;
352
}
353
354
/*
355
* Calculate the checksum of a disk block and store it
356
* at the indicated position.
357
*/
358
359
void
360
affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
361
{
362
int cnt = sb->s_blocksize / sizeof(__be32);
363
__be32 *ptr = (__be32 *)bh->b_data;
364
u32 checksum;
365
__be32 *checksumptr;
366
367
checksumptr = ptr + 5;
368
*checksumptr = 0;
369
for (checksum = 0; cnt > 0; ptr++, cnt--)
370
checksum += be32_to_cpu(*ptr);
371
*checksumptr = cpu_to_be32(-checksum);
372
}
373
374
void
375
secs_to_datestamp(time_t secs, struct affs_date *ds)
376
{
377
u32 days;
378
u32 minute;
379
380
secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
381
if (secs < 0)
382
secs = 0;
383
days = secs / 86400;
384
secs -= days * 86400;
385
minute = secs / 60;
386
secs -= minute * 60;
387
388
ds->days = cpu_to_be32(days);
389
ds->mins = cpu_to_be32(minute);
390
ds->ticks = cpu_to_be32(secs * 50);
391
}
392
393
mode_t
394
prot_to_mode(u32 prot)
395
{
396
int mode = 0;
397
398
if (!(prot & FIBF_NOWRITE))
399
mode |= S_IWUSR;
400
if (!(prot & FIBF_NOREAD))
401
mode |= S_IRUSR;
402
if (!(prot & FIBF_NOEXECUTE))
403
mode |= S_IXUSR;
404
if (prot & FIBF_GRP_WRITE)
405
mode |= S_IWGRP;
406
if (prot & FIBF_GRP_READ)
407
mode |= S_IRGRP;
408
if (prot & FIBF_GRP_EXECUTE)
409
mode |= S_IXGRP;
410
if (prot & FIBF_OTR_WRITE)
411
mode |= S_IWOTH;
412
if (prot & FIBF_OTR_READ)
413
mode |= S_IROTH;
414
if (prot & FIBF_OTR_EXECUTE)
415
mode |= S_IXOTH;
416
417
return mode;
418
}
419
420
void
421
mode_to_prot(struct inode *inode)
422
{
423
u32 prot = AFFS_I(inode)->i_protect;
424
mode_t mode = inode->i_mode;
425
426
if (!(mode & S_IXUSR))
427
prot |= FIBF_NOEXECUTE;
428
if (!(mode & S_IRUSR))
429
prot |= FIBF_NOREAD;
430
if (!(mode & S_IWUSR))
431
prot |= FIBF_NOWRITE;
432
if (mode & S_IXGRP)
433
prot |= FIBF_GRP_EXECUTE;
434
if (mode & S_IRGRP)
435
prot |= FIBF_GRP_READ;
436
if (mode & S_IWGRP)
437
prot |= FIBF_GRP_WRITE;
438
if (mode & S_IXOTH)
439
prot |= FIBF_OTR_EXECUTE;
440
if (mode & S_IROTH)
441
prot |= FIBF_OTR_READ;
442
if (mode & S_IWOTH)
443
prot |= FIBF_OTR_WRITE;
444
445
AFFS_I(inode)->i_protect = prot;
446
}
447
448
void
449
affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
450
{
451
va_list args;
452
453
va_start(args,fmt);
454
vsnprintf(ErrorBuffer,sizeof(ErrorBuffer),fmt,args);
455
va_end(args);
456
457
printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", sb->s_id,
458
function,ErrorBuffer);
459
if (!(sb->s_flags & MS_RDONLY))
460
printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
461
sb->s_flags |= MS_RDONLY;
462
}
463
464
void
465
affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
466
{
467
va_list args;
468
469
va_start(args,fmt);
470
vsnprintf(ErrorBuffer,sizeof(ErrorBuffer),fmt,args);
471
va_end(args);
472
473
printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", sb->s_id,
474
function,ErrorBuffer);
475
}
476
477
/* Check if the name is valid for a affs object. */
478
479
int
480
affs_check_name(const unsigned char *name, int len)
481
{
482
int i;
483
484
if (len > 30)
485
#ifdef AFFS_NO_TRUNCATE
486
return -ENAMETOOLONG;
487
#else
488
len = 30;
489
#endif
490
491
for (i = 0; i < len; i++) {
492
if (name[i] < ' ' || name[i] == ':'
493
|| (name[i] > 0x7e && name[i] < 0xa0))
494
return -EINVAL;
495
}
496
497
return 0;
498
}
499
500
/* This function copies name to bstr, with at most 30
501
* characters length. The bstr will be prepended by
502
* a length byte.
503
* NOTE: The name will must be already checked by
504
* affs_check_name()!
505
*/
506
507
int
508
affs_copy_name(unsigned char *bstr, struct dentry *dentry)
509
{
510
int len = min(dentry->d_name.len, 30u);
511
512
*bstr++ = len;
513
memcpy(bstr, dentry->d_name.name, len);
514
return len;
515
}
516
517