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   */
15  static DECLARE_RWSEM(adfs_dir_rwsem);
16  
adfs_dir_copyfrom(void * dst,struct adfs_dir * dir,unsigned int offset,size_t len)17  int 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  
adfs_dir_copyto(struct adfs_dir * dir,unsigned int offset,const void * src,size_t len)42  int 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  
__adfs_dir_cleanup(struct adfs_dir * dir)67  static 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  
adfs_dir_relse(struct adfs_dir * dir)77  void 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  
adfs_dir_forget(struct adfs_dir * dir)87  static 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  
adfs_dir_read_buffers(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)97  int 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  
140  error:
141  	adfs_dir_relse(dir);
142  
143  	return -EIO;
144  }
145  
adfs_dir_read(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)146  static 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  
adfs_dir_read_inode(struct super_block * sb,struct inode * inode,struct adfs_dir * dir)156  static 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  
adfs_dir_mark_dirty(struct adfs_dir * dir)176  static 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  
adfs_dir_sync(struct adfs_dir * dir)185  static 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  
adfs_object_fixup(struct adfs_dir * dir,struct object_info * obj)200  void 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  
adfs_iterate(struct file * file,struct dir_context * ctx)238  static 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  
264  unlock_relse:
265  	up_read(&adfs_dir_rwsem);
266  	adfs_dir_relse(&dir);
267  	return ret;
268  
269  unlock:
270  	up_read(&adfs_dir_rwsem);
271  	return ret;
272  }
273  
274  int
adfs_dir_update(struct super_block * sb,struct object_info * obj,int wait)275  adfs_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  	 */
314  forget:
315  	if (ret == -ENOENT)
316  		adfs_dir_relse(&dir);
317  	else
318  		adfs_dir_forget(&dir);
319  unlock:
320  	up_write(&adfs_dir_rwsem);
321  
322  	return ret;
323  }
324  
adfs_tolower(unsigned char c)325  static unsigned char adfs_tolower(unsigned char c)
326  {
327  	if (c >= 'A' && c <= 'Z')
328  		c += 'a' - 'A';
329  	return c;
330  }
331  
__adfs_compare(const unsigned char * qstr,u32 qlen,const char * str,u32 len)332  static 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  
adfs_dir_lookup_byname(struct inode * inode,const struct qstr * qstr,struct object_info * obj)347  static 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  
377  unlock_relse:
378  	up_read(&adfs_dir_rwsem);
379  	adfs_dir_relse(&dir);
380  	return ret;
381  
382  unlock:
383  	up_read(&adfs_dir_rwsem);
384  	return ret;
385  }
386  
387  const 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  
394  static int
adfs_hash(const struct dentry * parent,struct qstr * qstr)395  adfs_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   */
adfs_compare(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * qstr)418  static 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  
424  const struct dentry_operations adfs_dentry_operations = {
425  	.d_hash		= adfs_hash,
426  	.d_compare	= adfs_compare,
427  };
428  
429  static struct dentry *
adfs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)430  adfs_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   */
454  const struct inode_operations adfs_dir_inode_operations = {
455  	.lookup		= adfs_lookup,
456  	.setattr	= adfs_notify_change,
457  };
458