Linux Audio

Check our new training course

Loading...
v3.5.6
 
  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 *filp, void *dirent, filldir_t filldir)
 21{
 22	struct inode *inode = filp->f_path.dentry->d_inode;
 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 (filp->f_pos >> 32)
 30		goto out;
 
 
 
 
 
 
 
 
 
 
 
 31
 32	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 33	if (ret)
 34		goto out;
 35
 36	switch ((unsigned long)filp->f_pos) {
 37	case 0:
 38		if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
 39			goto free_out;
 40		filp->f_pos += 1;
 41
 42	case 1:
 43		if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0)
 44			goto free_out;
 45		filp->f_pos += 1;
 
 46
 47	default:
 48		break;
 
 
 
 
 
 
 
 
 
 
 49	}
 50
 51	read_lock(&adfs_dir_lock);
 52
 53	ret = ops->setpos(&dir, filp->f_pos - 2);
 54	if (ret)
 55		goto unlock_out;
 56	while (ops->getnext(&dir, &obj) == 0) {
 57		if (filldir(dirent, obj.name, obj.name_len,
 58			    filp->f_pos, obj.file_id, DT_UNKNOWN) < 0)
 59			goto unlock_out;
 60		filp->f_pos += 1;
 61	}
 62
 63unlock_out:
 64	read_unlock(&adfs_dir_lock);
 
 65
 66free_out:
 67	ops->free(&dir);
 
 
 
 68
 69out:
 70	return ret;
 
 
 
 
 
 
 71}
 72
 73int
 74adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 75{
 76	int ret = -EINVAL;
 77#ifdef CONFIG_ADFS_FS_RW
 78	struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 79	struct adfs_dir dir;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 80
 81	printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
 82		 obj->file_id, obj->parent_id);
 
 
 
 
 
 83
 84	if (!ops->update) {
 85		ret = -EINVAL;
 86		goto out;
 87	}
 
 88
 89	ret = ops->read(sb, obj->parent_id, 0, &dir);
 90	if (ret)
 91		goto out;
 92
 93	write_lock(&adfs_dir_lock);
 94	ret = ops->update(&dir, obj);
 95	write_unlock(&adfs_dir_lock);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 96
 97	if (wait) {
 98		int err = ops->sync(&dir);
 99		if (!ret)
100			ret = err;
 
 
 
 
 
 
101	}
102
103	ops->free(&dir);
104out:
105#endif
106	return ret;
107}
108
109static int
110adfs_match(struct qstr *name, struct object_info *obj)
 
 
 
 
 
 
 
 
111{
 
112	int i;
113
114	if (name->len != obj->name_len)
115		return 0;
 
 
 
 
 
 
 
 
 
 
 
116
117	for (i = 0; i < name->len; i++) {
118		char c1, c2;
 
 
 
 
 
 
 
 
 
 
 
 
119
120		c1 = name->name[i];
121		c2 = obj->name[i];
122
123		if (c1 >= 'A' && c1 <= 'Z')
124			c1 += 'a' - 'A';
125		if (c2 >= 'A' && c2 <= 'Z')
126			c2 += 'a' - 'A';
 
 
127
128		if (c1 != c2)
129			return 0;
 
 
 
 
130	}
131	return 1;
132}
133
134static int
135adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
136{
 
137	struct super_block *sb = inode->i_sb;
138	struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
139	struct adfs_dir dir;
140	int ret;
141
142	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 
143	if (ret)
144		goto out;
145
146	if (ADFS_I(inode)->parent_id != dir.parent_id) {
147		adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
148			   ADFS_I(inode)->parent_id, dir.parent_id);
149		ret = -EIO;
150		goto free_out;
 
 
 
 
151	}
152
153	obj->parent_id = inode->i_ino;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
155	/*
156	 * '.' is handled by reserved_lookup() in fs/namei.c
 
 
157	 */
158	if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
159		/*
160		 * Currently unable to fill in the rest of 'obj',
161		 * but this is better than nothing.  We need to
162		 * ascend one level to find it's parent.
163		 */
164		obj->name_len = 0;
165		obj->file_id  = obj->parent_id;
166		goto free_out;
167	}
168
169	read_lock(&adfs_dir_lock);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
171	ret = ops->setpos(&dir, 0);
172	if (ret)
173		goto unlock_out;
174
175	ret = -ENOENT;
 
 
176	while (ops->getnext(&dir, obj) == 0) {
177		if (adfs_match(name, obj)) {
178			ret = 0;
179			break;
180		}
181	}
 
182
183unlock_out:
184	read_unlock(&adfs_dir_lock);
 
 
185
186free_out:
187	ops->free(&dir);
188out:
189	return ret;
190}
191
192const struct file_operations adfs_dir_operations = {
193	.read		= generic_read_dir,
194	.llseek		= generic_file_llseek,
195	.readdir	= adfs_readdir,
196	.fsync		= generic_file_fsync,
197};
198
199static int
200adfs_hash(const struct dentry *parent, const struct inode *inode,
201		struct qstr *qstr)
202{
203	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
204	const unsigned char *name;
205	unsigned long hash;
206	int i;
207
208	if (qstr->len < name_len)
209		return 0;
210
211	/*
212	 * Truncate the name in place, avoids
213	 * having to define a compare function.
214	 */
215	qstr->len = i = name_len;
216	name = qstr->name;
217	hash = init_name_hash();
218	while (i--) {
219		char c;
220
221		c = *name++;
222		if (c >= 'A' && c <= 'Z')
223			c += 'a' - 'A';
224
225		hash = partial_name_hash(c, hash);
226	}
227	qstr->hash = end_name_hash(hash);
228
229	return 0;
230}
231
232/*
233 * Compare two names, taking note of the name length
234 * requirements of the underlying filesystem.
235 */
236static int
237adfs_compare(const struct dentry *parent, const struct inode *pinode,
238		const struct dentry *dentry, const struct inode *inode,
239		unsigned int len, const char *str, const struct qstr *name)
240{
241	int i;
242
243	if (len != name->len)
244		return 1;
245
246	for (i = 0; i < name->len; i++) {
247		char a, b;
248
249		a = str[i];
250		b = name->name[i];
251
252		if (a >= 'A' && a <= 'Z')
253			a += 'a' - 'A';
254		if (b >= 'A' && b <= 'Z')
255			b += 'a' - 'A';
256
257		if (a != b)
258			return 1;
259	}
260	return 0;
261}
262
263const struct dentry_operations adfs_dentry_operations = {
264	.d_hash		= adfs_hash,
265	.d_compare	= adfs_compare,
266};
267
268static struct dentry *
269adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
270{
271	struct inode *inode = NULL;
272	struct object_info obj;
273	int error;
274
275	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
276	if (error == 0) {
277		error = -EACCES;
278		/*
279		 * This only returns NULL if get_empty_inode
280		 * fails.
281		 */
282		inode = adfs_iget(dir->i_sb, &obj);
283		if (inode)
284			error = 0;
 
 
285	}
286	d_add(dentry, inode);
287	return ERR_PTR(error);
288}
289
290/*
291 * directories can handle most operations...
292 */
293const struct inode_operations adfs_dir_inode_operations = {
294	.lookup		= adfs_lookup,
295	.setattr	= adfs_notify_change,
296};
v6.13.7
  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};