Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
v3.15
 
  1/*
  2 *  linux/fs/adfs/dir.c
  3 *
  4 *  Copyright (C) 1999-2000 Russell King
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 *
 10 *  Common directory handling for ADFS
 11 */
 
 12#include "adfs.h"
 13
 14/*
 15 * For future.  This should probably be per-directory.
 16 */
 17static DEFINE_RWLOCK(adfs_dir_lock);
 18
 19static int
 20adfs_readdir(struct file *file, struct dir_context *ctx)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 21{
 22	struct inode *inode = file_inode(file);
 23	struct super_block *sb = inode->i_sb;
 24	struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 25	struct object_info obj;
 26	struct adfs_dir dir;
 27	int ret = 0;
 28
 29	if (ctx->pos >> 32)
 30		return 0;
 31
 32	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 
 33	if (ret)
 34		return ret;
 35
 36	if (ctx->pos == 0) {
 37		if (!dir_emit_dot(file, ctx))
 38			goto free_out;
 39		ctx->pos = 1;
 40	}
 41	if (ctx->pos == 1) {
 42		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
 43			goto free_out;
 44		ctx->pos = 2;
 45	}
 46
 47	read_lock(&adfs_dir_lock);
 48
 49	ret = ops->setpos(&dir, ctx->pos - 2);
 50	if (ret)
 51		goto unlock_out;
 52	while (ops->getnext(&dir, &obj) == 0) {
 53		if (!dir_emit(ctx, obj.name, obj.name_len,
 54			    obj.file_id, DT_UNKNOWN))
 55			break;
 56		ctx->pos++;
 57	}
 58
 59unlock_out:
 60	read_unlock(&adfs_dir_lock);
 
 
 61
 62free_out:
 63	ops->free(&dir);
 64	return ret;
 65}
 66
 67int
 68adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 69{
 70	int ret = -EINVAL;
 71#ifdef CONFIG_ADFS_FS_RW
 72	struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 73	struct adfs_dir dir;
 
 74
 75	printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
 76		 obj->file_id, obj->parent_id);
 77
 78	if (!ops->update) {
 79		ret = -EINVAL;
 80		goto out;
 81	}
 82
 83	ret = ops->read(sb, obj->parent_id, 0, &dir);
 
 84	if (ret)
 85		goto out;
 86
 87	write_lock(&adfs_dir_lock);
 88	ret = ops->update(&dir, obj);
 89	write_unlock(&adfs_dir_lock);
 
 90
 91	if (wait) {
 92		int err = ops->sync(&dir);
 93		if (!ret)
 94			ret = err;
 95	}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 96
 97	ops->free(&dir);
 98out:
 99#endif
100	return ret;
101}
102
103static int
104adfs_match(struct qstr *name, struct object_info *obj)
105{
106	int i;
107
108	if (name->len != obj->name_len)
109		return 0;
110
111	for (i = 0; i < name->len; i++) {
112		char c1, c2;
 
 
113
114		c1 = name->name[i];
115		c2 = obj->name[i];
116
117		if (c1 >= 'A' && c1 <= 'Z')
118			c1 += 'a' - 'A';
119		if (c2 >= 'A' && c2 <= 'Z')
120			c2 += 'a' - 'A';
121
122		if (c1 != c2)
123			return 0;
124	}
125	return 1;
126}
127
128static int
129adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
130{
131	struct super_block *sb = inode->i_sb;
132	struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 
133	struct adfs_dir dir;
 
134	int ret;
135
136	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 
137	if (ret)
138		goto out;
139
140	if (ADFS_I(inode)->parent_id != dir.parent_id) {
141		adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
142			   ADFS_I(inode)->parent_id, dir.parent_id);
143		ret = -EIO;
144		goto free_out;
145	}
146
147	obj->parent_id = inode->i_ino;
148
149	/*
150	 * '.' is handled by reserved_lookup() in fs/namei.c
151	 */
152	if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
153		/*
154		 * Currently unable to fill in the rest of 'obj',
155		 * but this is better than nothing.  We need to
156		 * ascend one level to find it's parent.
157		 */
158		obj->name_len = 0;
159		obj->file_id  = obj->parent_id;
160		goto free_out;
161	}
162
163	read_lock(&adfs_dir_lock);
164
165	ret = ops->setpos(&dir, 0);
166	if (ret)
167		goto unlock_out;
168
169	ret = -ENOENT;
 
 
170	while (ops->getnext(&dir, obj) == 0) {
171		if (adfs_match(name, obj)) {
172			ret = 0;
173			break;
174		}
175	}
 
176
177unlock_out:
178	read_unlock(&adfs_dir_lock);
 
 
179
180free_out:
181	ops->free(&dir);
182out:
183	return ret;
184}
185
186const struct file_operations adfs_dir_operations = {
187	.read		= generic_read_dir,
188	.llseek		= generic_file_llseek,
189	.iterate	= adfs_readdir,
190	.fsync		= generic_file_fsync,
191};
192
193static int
194adfs_hash(const struct dentry *parent, struct qstr *qstr)
195{
196	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
197	const unsigned char *name;
198	unsigned long hash;
199	int i;
200
201	if (qstr->len < name_len)
202		return 0;
203
204	/*
205	 * Truncate the name in place, avoids
206	 * having to define a compare function.
207	 */
208	qstr->len = i = name_len;
209	name = qstr->name;
210	hash = init_name_hash();
211	while (i--) {
212		char c;
213
214		c = *name++;
215		if (c >= 'A' && c <= 'Z')
216			c += 'a' - 'A';
217
218		hash = partial_name_hash(c, hash);
219	}
220	qstr->hash = end_name_hash(hash);
221
222	return 0;
223}
224
225/*
226 * Compare two names, taking note of the name length
227 * requirements of the underlying filesystem.
228 */
229static int
230adfs_compare(const struct dentry *parent, const struct dentry *dentry,
231		unsigned int len, const char *str, const struct qstr *name)
232{
233	int i;
234
235	if (len != name->len)
236		return 1;
237
238	for (i = 0; i < name->len; i++) {
239		char a, b;
240
241		a = str[i];
242		b = name->name[i];
243
244		if (a >= 'A' && a <= 'Z')
245			a += 'a' - 'A';
246		if (b >= 'A' && b <= 'Z')
247			b += 'a' - 'A';
248
249		if (a != b)
250			return 1;
251	}
252	return 0;
253}
254
255const struct dentry_operations adfs_dentry_operations = {
256	.d_hash		= adfs_hash,
257	.d_compare	= adfs_compare,
258};
259
260static struct dentry *
261adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
262{
263	struct inode *inode = NULL;
264	struct object_info obj;
265	int error;
266
267	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
268	if (error == 0) {
269		error = -EACCES;
270		/*
271		 * This only returns NULL if get_empty_inode
272		 * fails.
273		 */
274		inode = adfs_iget(dir->i_sb, &obj);
275		if (inode)
276			error = 0;
 
 
277	}
278	d_add(dentry, inode);
279	return ERR_PTR(error);
280}
281
282/*
283 * directories can handle most operations...
284 */
285const struct inode_operations adfs_dir_inode_operations = {
286	.lookup		= adfs_lookup,
287	.setattr	= adfs_notify_change,
288};
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  linux/fs/adfs/dir.c
  4 *
  5 *  Copyright (C) 1999-2000 Russell King
  6 *
 
 
 
 
  7 *  Common directory handling for ADFS
  8 */
  9#include <linux/slab.h>
 10#include "adfs.h"
 11
 12/*
 13 * For future.  This should probably be per-directory.
 14 */
 15static DECLARE_RWSEM(adfs_dir_rwsem);
 16
 17int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
 18		      size_t len)
 19{
 20	struct super_block *sb = dir->sb;
 21	unsigned int index, remain;
 22
 23	index = offset >> sb->s_blocksize_bits;
 24	offset &= sb->s_blocksize - 1;
 25	remain = sb->s_blocksize - offset;
 26	if (index + (remain < len) >= dir->nr_buffers)
 27		return -EINVAL;
 28
 29	if (remain < len) {
 30		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
 31		dst += remain;
 32		len -= remain;
 33		index += 1;
 34		offset = 0;
 35	}
 36
 37	memcpy(dst, dir->bhs[index]->b_data + offset, len);
 38
 39	return 0;
 40}
 41
 42int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
 43		    size_t len)
 44{
 45	struct super_block *sb = dir->sb;
 46	unsigned int index, remain;
 47
 48	index = offset >> sb->s_blocksize_bits;
 49	offset &= sb->s_blocksize - 1;
 50	remain = sb->s_blocksize - offset;
 51	if (index + (remain < len) >= dir->nr_buffers)
 52		return -EINVAL;
 53
 54	if (remain < len) {
 55		memcpy(dir->bhs[index]->b_data + offset, src, remain);
 56		src += remain;
 57		len -= remain;
 58		index += 1;
 59		offset = 0;
 60	}
 61
 62	memcpy(dir->bhs[index]->b_data + offset, src, len);
 63
 64	return 0;
 65}
 66
 67static void __adfs_dir_cleanup(struct adfs_dir *dir)
 68{
 69	dir->nr_buffers = 0;
 70
 71	if (dir->bhs != dir->bh)
 72		kfree(dir->bhs);
 73	dir->bhs = NULL;
 74	dir->sb = NULL;
 75}
 76
 77void adfs_dir_relse(struct adfs_dir *dir)
 78{
 79	unsigned int i;
 80
 81	for (i = 0; i < dir->nr_buffers; i++)
 82		brelse(dir->bhs[i]);
 83
 84	__adfs_dir_cleanup(dir);
 85}
 86
 87static void adfs_dir_forget(struct adfs_dir *dir)
 88{
 89	unsigned int i;
 90
 91	for (i = 0; i < dir->nr_buffers; i++)
 92		bforget(dir->bhs[i]);
 93
 94	__adfs_dir_cleanup(dir);
 95}
 96
 97int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
 98			  unsigned int size, struct adfs_dir *dir)
 99{
100	struct buffer_head **bhs;
101	unsigned int i, num;
102	int block;
103
104	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
105	if (num > ARRAY_SIZE(dir->bh)) {
106		/* We only allow one extension */
107		if (dir->bhs != dir->bh)
108			return -EINVAL;
109
110		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
111		if (!bhs)
112			return -ENOMEM;
113
114		if (dir->nr_buffers)
115			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
116
117		dir->bhs = bhs;
118	}
119
120	for (i = dir->nr_buffers; i < num; i++) {
121		block = __adfs_block_map(sb, indaddr, i);
122		if (!block) {
123			adfs_error(sb, "dir %06x has a hole at offset %u",
124				   indaddr, i);
125			goto error;
126		}
127
128		dir->bhs[i] = sb_bread(sb, block);
129		if (!dir->bhs[i]) {
130			adfs_error(sb,
131				   "dir %06x failed read at offset %u, mapped block 0x%08x",
132				   indaddr, i, block);
133			goto error;
134		}
135
136		dir->nr_buffers++;
137	}
138	return 0;
139
140error:
141	adfs_dir_relse(dir);
142
143	return -EIO;
144}
145
146static int adfs_dir_read(struct super_block *sb, u32 indaddr,
147			 unsigned int size, struct adfs_dir *dir)
148{
149	dir->sb = sb;
150	dir->bhs = dir->bh;
151	dir->nr_buffers = 0;
152
153	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
154}
155
156static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
157			       struct adfs_dir *dir)
158{
159	int ret;
160
161	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
162	if (ret)
163		return ret;
164
165	if (ADFS_I(inode)->parent_id != dir->parent_id) {
166		adfs_error(sb,
167			   "parent directory id changed under me! (%06x but got %06x)\n",
168			   ADFS_I(inode)->parent_id, dir->parent_id);
169		adfs_dir_relse(dir);
170		ret = -EIO;
171	}
172
173	return ret;
174}
175
176static void adfs_dir_mark_dirty(struct adfs_dir *dir)
177{
178	unsigned int i;
179
180	/* Mark the buffers dirty */
181	for (i = 0; i < dir->nr_buffers; i++)
182		mark_buffer_dirty(dir->bhs[i]);
183}
184
185static int adfs_dir_sync(struct adfs_dir *dir)
186{
187	int err = 0;
188	int i;
189
190	for (i = dir->nr_buffers - 1; i >= 0; i--) {
191		struct buffer_head *bh = dir->bhs[i];
192		sync_dirty_buffer(bh);
193		if (buffer_req(bh) && !buffer_uptodate(bh))
194			err = -EIO;
195	}
196
197	return err;
198}
199
200void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
201{
202	unsigned int dots, i;
203
204	/*
205	 * RISC OS allows the use of '/' in directory entry names, so we need
206	 * to fix these up.  '/' is typically used for FAT compatibility to
207	 * represent '.', so do the same conversion here.  In any case, '.'
208	 * will never be in a RISC OS name since it is used as the pathname
209	 * separator.  Handle the case where we may generate a '.' or '..'
210	 * name, replacing the first character with '^' (the RISC OS "parent
211	 * directory" character.)
212	 */
213	for (i = dots = 0; i < obj->name_len; i++)
214		if (obj->name[i] == '/') {
215			obj->name[i] = '.';
216			dots++;
217		}
218
219	if (obj->name_len <= 2 && dots == obj->name_len)
220		obj->name[0] = '^';
221
222	/*
223	 * If the object is a file, and the user requested the ,xyz hex
224	 * filetype suffix to the name, check the filetype and append.
225	 */
226	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
227		u16 filetype = adfs_filetype(obj->loadaddr);
228
229		if (filetype != ADFS_FILETYPE_NONE) {
230			obj->name[obj->name_len++] = ',';
231			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
232			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
233			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
234		}
235	}
236}
237
238static int adfs_iterate(struct file *file, struct dir_context *ctx)
239{
240	struct inode *inode = file_inode(file);
241	struct super_block *sb = inode->i_sb;
242	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 
243	struct adfs_dir dir;
244	int ret;
 
 
 
245
246	down_read(&adfs_dir_rwsem);
247	ret = adfs_dir_read_inode(sb, inode, &dir);
248	if (ret)
249		goto unlock;
250
251	if (ctx->pos == 0) {
252		if (!dir_emit_dot(file, ctx))
253			goto unlock_relse;
254		ctx->pos = 1;
255	}
256	if (ctx->pos == 1) {
257		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
258			goto unlock_relse;
259		ctx->pos = 2;
260	}
261
262	ret = ops->iterate(&dir, ctx);
 
 
 
 
 
 
 
 
 
 
263
264unlock_relse:
265	up_read(&adfs_dir_rwsem);
266	adfs_dir_relse(&dir);
267	return ret;
268
269unlock:
270	up_read(&adfs_dir_rwsem);
271	return ret;
272}
273
274int
275adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
276{
277	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 
 
278	struct adfs_dir dir;
279	int ret;
280
281	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
282		return -EINVAL;
283
284	if (!ops->update)
285		return -EINVAL;
 
 
286
287	down_write(&adfs_dir_rwsem);
288	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
289	if (ret)
290		goto unlock;
291
 
292	ret = ops->update(&dir, obj);
293	if (ret)
294		goto forget;
295
296	ret = ops->commit(&dir);
297	if (ret)
298		goto forget;
299	up_write(&adfs_dir_rwsem);
300
301	adfs_dir_mark_dirty(&dir);
302
303	if (wait)
304		ret = adfs_dir_sync(&dir);
305
306	adfs_dir_relse(&dir);
307	return ret;
308
309	/*
310	 * If the updated failed because the entry wasn't found, we can
311	 * just release the buffers. If it was any other error, forget
312	 * the dirtied buffers so they aren't written back to the media.
313	 */
314forget:
315	if (ret == -ENOENT)
316		adfs_dir_relse(&dir);
317	else
318		adfs_dir_forget(&dir);
319unlock:
320	up_write(&adfs_dir_rwsem);
321
 
 
 
322	return ret;
323}
324
325static unsigned char adfs_tolower(unsigned char c)
 
326{
327	if (c >= 'A' && c <= 'Z')
328		c += 'a' - 'A';
329	return c;
330}
331
332static int __adfs_compare(const unsigned char *qstr, u32 qlen,
333			  const char *str, u32 len)
334{
335	u32 i;
336
337	if (qlen != len)
338		return 1;
339
340	for (i = 0; i < qlen; i++)
341		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
342			return 1;
 
343
344	return 0;
 
 
 
345}
346
347static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
348				  struct object_info *obj)
349{
350	struct super_block *sb = inode->i_sb;
351	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
352	const unsigned char *name;
353	struct adfs_dir dir;
354	u32 name_len;
355	int ret;
356
357	down_read(&adfs_dir_rwsem);
358	ret = adfs_dir_read_inode(sb, inode, &dir);
359	if (ret)
360		goto unlock;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
362	ret = ops->setpos(&dir, 0);
363	if (ret)
364		goto unlock_relse;
365
366	ret = -ENOENT;
367	name = qstr->name;
368	name_len = qstr->len;
369	while (ops->getnext(&dir, obj) == 0) {
370		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
371			ret = 0;
372			break;
373		}
374	}
375	obj->parent_id = ADFS_I(inode)->indaddr;
376
377unlock_relse:
378	up_read(&adfs_dir_rwsem);
379	adfs_dir_relse(&dir);
380	return ret;
381
382unlock:
383	up_read(&adfs_dir_rwsem);
 
384	return ret;
385}
386
387const struct file_operations adfs_dir_operations = {
388	.read		= generic_read_dir,
389	.llseek		= generic_file_llseek,
390	.iterate_shared	= adfs_iterate,
391	.fsync		= generic_file_fsync,
392};
393
394static int
395adfs_hash(const struct dentry *parent, struct qstr *qstr)
396{
 
397	const unsigned char *name;
398	unsigned long hash;
399	u32 len;
400
401	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
402		return -ENAMETOOLONG;
403
404	len = qstr->len;
 
 
 
 
405	name = qstr->name;
406	hash = init_name_hash(parent);
407	while (len--)
408		hash = partial_name_hash(adfs_tolower(*name++), hash);
 
 
 
 
 
 
 
409	qstr->hash = end_name_hash(hash);
410
411	return 0;
412}
413
414/*
415 * Compare two names, taking note of the name length
416 * requirements of the underlying filesystem.
417 */
418static int adfs_compare(const struct dentry *dentry, unsigned int len,
419			const char *str, const struct qstr *qstr)
 
420{
421	return __adfs_compare(qstr->name, qstr->len, str, len);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422}
423
424const struct dentry_operations adfs_dentry_operations = {
425	.d_hash		= adfs_hash,
426	.d_compare	= adfs_compare,
427};
428
429static struct dentry *
430adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
431{
432	struct inode *inode = NULL;
433	struct object_info obj;
434	int error;
435
436	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
437	if (error == 0) {
 
438		/*
439		 * This only returns NULL if get_empty_inode
440		 * fails.
441		 */
442		inode = adfs_iget(dir->i_sb, &obj);
443		if (!inode)
444			inode = ERR_PTR(-EACCES);
445	} else if (error != -ENOENT) {
446		inode = ERR_PTR(error);
447	}
448	return d_splice_alias(inode, dentry);
 
449}
450
451/*
452 * directories can handle most operations...
453 */
454const struct inode_operations adfs_dir_inode_operations = {
455	.lookup		= adfs_lookup,
456	.setattr	= adfs_notify_change,
457};