Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/affs/namei.c
15109 views
1
/*
2
* linux/fs/affs/namei.c
3
*
4
* (c) 1996 Hans-Joachim Widmaier - Rewritten
5
*
6
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
7
*
8
* (C) 1991 Linus Torvalds - minix filesystem
9
*/
10
11
#include "affs.h"
12
13
typedef int (*toupper_t)(int);
14
15
static int affs_toupper(int ch);
16
static int affs_hash_dentry(const struct dentry *,
17
const struct inode *, struct qstr *);
18
static int affs_compare_dentry(const struct dentry *parent,
19
const struct inode *pinode,
20
const struct dentry *dentry, const struct inode *inode,
21
unsigned int len, const char *str, const struct qstr *name);
22
static int affs_intl_toupper(int ch);
23
static int affs_intl_hash_dentry(const struct dentry *,
24
const struct inode *, struct qstr *);
25
static int affs_intl_compare_dentry(const struct dentry *parent,
26
const struct inode *pinode,
27
const struct dentry *dentry, const struct inode *inode,
28
unsigned int len, const char *str, const struct qstr *name);
29
30
const struct dentry_operations affs_dentry_operations = {
31
.d_hash = affs_hash_dentry,
32
.d_compare = affs_compare_dentry,
33
};
34
35
const struct dentry_operations affs_intl_dentry_operations = {
36
.d_hash = affs_intl_hash_dentry,
37
.d_compare = affs_intl_compare_dentry,
38
};
39
40
41
/* Simple toupper() for DOS\1 */
42
43
static int
44
affs_toupper(int ch)
45
{
46
return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
47
}
48
49
/* International toupper() for DOS\3 ("international") */
50
51
static int
52
affs_intl_toupper(int ch)
53
{
54
return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
55
&& ch <= 0xFE && ch != 0xF7) ?
56
ch - ('a' - 'A') : ch;
57
}
58
59
static inline toupper_t
60
affs_get_toupper(struct super_block *sb)
61
{
62
return AFFS_SB(sb)->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
63
}
64
65
/*
66
* Note: the dentry argument is the parent dentry.
67
*/
68
static inline int
69
__affs_hash_dentry(struct qstr *qstr, toupper_t toupper)
70
{
71
const u8 *name = qstr->name;
72
unsigned long hash;
73
int i;
74
75
i = affs_check_name(qstr->name, qstr->len);
76
if (i)
77
return i;
78
79
hash = init_name_hash();
80
i = min(qstr->len, 30u);
81
for (; i > 0; name++, i--)
82
hash = partial_name_hash(toupper(*name), hash);
83
qstr->hash = end_name_hash(hash);
84
85
return 0;
86
}
87
88
static int
89
affs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
90
struct qstr *qstr)
91
{
92
return __affs_hash_dentry(qstr, affs_toupper);
93
}
94
static int
95
affs_intl_hash_dentry(const struct dentry *dentry, const struct inode *inode,
96
struct qstr *qstr)
97
{
98
return __affs_hash_dentry(qstr, affs_intl_toupper);
99
}
100
101
static inline int __affs_compare_dentry(unsigned int len,
102
const char *str, const struct qstr *name, toupper_t toupper)
103
{
104
const u8 *aname = str;
105
const u8 *bname = name->name;
106
107
/*
108
* 'str' is the name of an already existing dentry, so the name
109
* must be valid. 'name' must be validated first.
110
*/
111
112
if (affs_check_name(name->name, name->len))
113
return 1;
114
115
/*
116
* If the names are longer than the allowed 30 chars,
117
* the excess is ignored, so their length may differ.
118
*/
119
if (len >= 30) {
120
if (name->len < 30)
121
return 1;
122
len = 30;
123
} else if (len != name->len)
124
return 1;
125
126
for (; len > 0; len--)
127
if (toupper(*aname++) != toupper(*bname++))
128
return 1;
129
130
return 0;
131
}
132
133
static int
134
affs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
135
const struct dentry *dentry, const struct inode *inode,
136
unsigned int len, const char *str, const struct qstr *name)
137
{
138
return __affs_compare_dentry(len, str, name, affs_toupper);
139
}
140
static int
141
affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode,
142
const struct dentry *dentry, const struct inode *inode,
143
unsigned int len, const char *str, const struct qstr *name)
144
{
145
return __affs_compare_dentry(len, str, name, affs_intl_toupper);
146
}
147
148
/*
149
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
150
*/
151
152
static inline int
153
affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
154
{
155
const u8 *name = dentry->d_name.name;
156
int len = dentry->d_name.len;
157
158
if (len >= 30) {
159
if (*name2 < 30)
160
return 0;
161
len = 30;
162
} else if (len != *name2)
163
return 0;
164
165
for (name2++; len > 0; len--)
166
if (toupper(*name++) != toupper(*name2++))
167
return 0;
168
return 1;
169
}
170
171
int
172
affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
173
{
174
toupper_t toupper = affs_get_toupper(sb);
175
int hash;
176
177
hash = len = min(len, 30u);
178
for (; len > 0; len--)
179
hash = (hash * 13 + toupper(*name++)) & 0x7ff;
180
181
return hash % AFFS_SB(sb)->s_hashsize;
182
}
183
184
static struct buffer_head *
185
affs_find_entry(struct inode *dir, struct dentry *dentry)
186
{
187
struct super_block *sb = dir->i_sb;
188
struct buffer_head *bh;
189
toupper_t toupper = affs_get_toupper(sb);
190
u32 key;
191
192
pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
193
194
bh = affs_bread(sb, dir->i_ino);
195
if (!bh)
196
return ERR_PTR(-EIO);
197
198
key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
199
200
for (;;) {
201
affs_brelse(bh);
202
if (key == 0)
203
return NULL;
204
bh = affs_bread(sb, key);
205
if (!bh)
206
return ERR_PTR(-EIO);
207
if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
208
return bh;
209
key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
210
}
211
}
212
213
struct dentry *
214
affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
215
{
216
struct super_block *sb = dir->i_sb;
217
struct buffer_head *bh;
218
struct inode *inode = NULL;
219
220
pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
221
222
affs_lock_dir(dir);
223
bh = affs_find_entry(dir, dentry);
224
affs_unlock_dir(dir);
225
if (IS_ERR(bh))
226
return ERR_CAST(bh);
227
if (bh) {
228
u32 ino = bh->b_blocknr;
229
230
/* store the real header ino in d_fsdata for faster lookups */
231
dentry->d_fsdata = (void *)(long)ino;
232
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
233
//link to dirs disabled
234
//case ST_LINKDIR:
235
case ST_LINKFILE:
236
ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
237
}
238
affs_brelse(bh);
239
inode = affs_iget(sb, ino);
240
if (IS_ERR(inode))
241
return ERR_CAST(inode);
242
}
243
d_add(dentry, inode);
244
return NULL;
245
}
246
247
int
248
affs_unlink(struct inode *dir, struct dentry *dentry)
249
{
250
pr_debug("AFFS: unlink(dir=%d, %lu \"%.*s\")\n", (u32)dir->i_ino,
251
dentry->d_inode->i_ino,
252
(int)dentry->d_name.len, dentry->d_name.name);
253
254
return affs_remove_header(dentry);
255
}
256
257
int
258
affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
259
{
260
struct super_block *sb = dir->i_sb;
261
struct inode *inode;
262
int error;
263
264
pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
265
dentry->d_name.name,mode);
266
267
inode = affs_new_inode(dir);
268
if (!inode)
269
return -ENOSPC;
270
271
inode->i_mode = mode;
272
mode_to_prot(inode);
273
mark_inode_dirty(inode);
274
275
inode->i_op = &affs_file_inode_operations;
276
inode->i_fop = &affs_file_operations;
277
inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
278
error = affs_add_entry(dir, inode, dentry, ST_FILE);
279
if (error) {
280
inode->i_nlink = 0;
281
iput(inode);
282
return error;
283
}
284
return 0;
285
}
286
287
int
288
affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
289
{
290
struct inode *inode;
291
int error;
292
293
pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
294
(int)dentry->d_name.len,dentry->d_name.name,mode);
295
296
inode = affs_new_inode(dir);
297
if (!inode)
298
return -ENOSPC;
299
300
inode->i_mode = S_IFDIR | mode;
301
mode_to_prot(inode);
302
303
inode->i_op = &affs_dir_inode_operations;
304
inode->i_fop = &affs_dir_operations;
305
306
error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
307
if (error) {
308
inode->i_nlink = 0;
309
mark_inode_dirty(inode);
310
iput(inode);
311
return error;
312
}
313
return 0;
314
}
315
316
int
317
affs_rmdir(struct inode *dir, struct dentry *dentry)
318
{
319
pr_debug("AFFS: rmdir(dir=%u, %lu \"%.*s\")\n", (u32)dir->i_ino,
320
dentry->d_inode->i_ino,
321
(int)dentry->d_name.len, dentry->d_name.name);
322
323
return affs_remove_header(dentry);
324
}
325
326
int
327
affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
328
{
329
struct super_block *sb = dir->i_sb;
330
struct buffer_head *bh;
331
struct inode *inode;
332
char *p;
333
int i, maxlen, error;
334
char c, lc;
335
336
pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
337
(int)dentry->d_name.len,dentry->d_name.name,symname);
338
339
maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1;
340
inode = affs_new_inode(dir);
341
if (!inode)
342
return -ENOSPC;
343
344
inode->i_op = &affs_symlink_inode_operations;
345
inode->i_data.a_ops = &affs_symlink_aops;
346
inode->i_mode = S_IFLNK | 0777;
347
mode_to_prot(inode);
348
349
error = -EIO;
350
bh = affs_bread(sb, inode->i_ino);
351
if (!bh)
352
goto err;
353
i = 0;
354
p = (char *)AFFS_HEAD(bh)->table;
355
lc = '/';
356
if (*symname == '/') {
357
struct affs_sb_info *sbi = AFFS_SB(sb);
358
while (*symname == '/')
359
symname++;
360
spin_lock(&sbi->symlink_lock);
361
while (sbi->s_volume[i]) /* Cannot overflow */
362
*p++ = sbi->s_volume[i++];
363
spin_unlock(&sbi->symlink_lock);
364
}
365
while (i < maxlen && (c = *symname++)) {
366
if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
367
*p++ = '/';
368
i++;
369
symname += 2;
370
lc = '/';
371
} else if (c == '.' && lc == '/' && *symname == '/') {
372
symname++;
373
lc = '/';
374
} else {
375
*p++ = c;
376
lc = c;
377
i++;
378
}
379
if (lc == '/')
380
while (*symname == '/')
381
symname++;
382
}
383
*p = 0;
384
mark_buffer_dirty_inode(bh, inode);
385
affs_brelse(bh);
386
mark_inode_dirty(inode);
387
388
error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
389
if (error)
390
goto err;
391
392
return 0;
393
394
err:
395
inode->i_nlink = 0;
396
mark_inode_dirty(inode);
397
iput(inode);
398
return error;
399
}
400
401
int
402
affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
403
{
404
struct inode *inode = old_dentry->d_inode;
405
406
pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
407
(int)dentry->d_name.len,dentry->d_name.name);
408
409
return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
410
}
411
412
int
413
affs_rename(struct inode *old_dir, struct dentry *old_dentry,
414
struct inode *new_dir, struct dentry *new_dentry)
415
{
416
struct super_block *sb = old_dir->i_sb;
417
struct buffer_head *bh = NULL;
418
int retval;
419
420
pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
421
(u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
422
(u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
423
424
retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len);
425
if (retval)
426
return retval;
427
428
/* Unlink destination if it already exists */
429
if (new_dentry->d_inode) {
430
retval = affs_remove_header(new_dentry);
431
if (retval)
432
return retval;
433
}
434
435
bh = affs_bread(sb, old_dentry->d_inode->i_ino);
436
if (!bh)
437
return -EIO;
438
439
/* Remove header from its parent directory. */
440
affs_lock_dir(old_dir);
441
retval = affs_remove_hash(old_dir, bh);
442
affs_unlock_dir(old_dir);
443
if (retval)
444
goto done;
445
446
/* And insert it into the new directory with the new name. */
447
affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
448
affs_fix_checksum(sb, bh);
449
affs_lock_dir(new_dir);
450
retval = affs_insert_hash(new_dir, bh);
451
affs_unlock_dir(new_dir);
452
/* TODO: move it back to old_dir, if error? */
453
454
done:
455
mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
456
affs_brelse(bh);
457
return retval;
458
}
459
460