xref: /linux/fs/affs/dir.c (revision bb9ae1a66c85eeb626864efd812c62026e126ec0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  linux/fs/affs/dir.c
4  *
5  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
6  *
7  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
8  *
9  *  (C) 1992  Eric Youngdale Modified for ISO 9660 filesystem.
10  *
11  *  (C) 1991  Linus Torvalds - minix filesystem
12  *
13  *  affs directory handling functions
14  *
15  */
16 
17 #include <linux/iversion.h>
18 #include "affs.h"
19 
20 struct affs_dir_data {
21 	unsigned long ino;
22 	u64 cookie;
23 };
24 
25 static int affs_readdir(struct file *, struct dir_context *);
26 
27 static loff_t affs_dir_llseek(struct file *file, loff_t offset, int whence)
28 {
29 	struct affs_dir_data *data = file->private_data;
30 
31 	return generic_llseek_cookie(file, offset, whence, &data->cookie);
32 }
33 
34 static int affs_dir_open(struct inode *inode, struct file *file)
35 {
36 	struct affs_dir_data	*data;
37 
38 	data = kzalloc(sizeof(struct affs_dir_data), GFP_KERNEL);
39 	if (!data)
40 		return -ENOMEM;
41 	file->private_data = data;
42 	return 0;
43 }
44 
45 static int affs_dir_release(struct inode *inode, struct file *file)
46 {
47 	kfree(file->private_data);
48 	return 0;
49 }
50 
51 const struct file_operations affs_dir_operations = {
52 	.open		= affs_dir_open,
53 	.read		= generic_read_dir,
54 	.llseek		= affs_dir_llseek,
55 	.iterate_shared	= affs_readdir,
56 	.fsync		= affs_file_fsync,
57 	.release	= affs_dir_release,
58 };
59 
60 /*
61  * directories can handle most operations...
62  */
63 const struct inode_operations affs_dir_inode_operations = {
64 	.create		= affs_create,
65 	.lookup		= affs_lookup,
66 	.link		= affs_link,
67 	.unlink		= affs_unlink,
68 	.symlink	= affs_symlink,
69 	.mkdir		= affs_mkdir,
70 	.rmdir		= affs_rmdir,
71 	.rename		= affs_rename2,
72 	.setattr	= affs_notify_change,
73 };
74 
75 static int
76 affs_readdir(struct file *file, struct dir_context *ctx)
77 {
78 	struct inode		*inode = file_inode(file);
79 	struct affs_dir_data	*data = file->private_data;
80 	struct super_block	*sb = inode->i_sb;
81 	struct buffer_head	*dir_bh = NULL;
82 	struct buffer_head	*fh_bh = NULL;
83 	unsigned char		*name;
84 	int			 namelen;
85 	u32			 i;
86 	int			 hash_pos;
87 	int			 chain_pos;
88 	u32			 ino;
89 	int			 error = 0;
90 
91 	pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos);
92 
93 	if (ctx->pos < 2) {
94 		data->ino = 0;
95 		if (!dir_emit_dots(file, ctx))
96 			return 0;
97 	}
98 
99 	affs_lock_dir(inode);
100 	chain_pos = (ctx->pos - 2) & 0xffff;
101 	hash_pos  = (ctx->pos - 2) >> 16;
102 	if (chain_pos == 0xffff) {
103 		affs_warning(sb, "readdir", "More than 65535 entries in chain");
104 		chain_pos = 0;
105 		hash_pos++;
106 		ctx->pos = ((hash_pos << 16) | chain_pos) + 2;
107 	}
108 	dir_bh = affs_bread(sb, inode->i_ino);
109 	if (!dir_bh)
110 		goto out_unlock_dir;
111 
112 	/* If the directory hasn't changed since the last call to readdir(),
113 	 * we can jump directly to where we left off.
114 	 */
115 	ino = data->ino;
116 	if (ino && inode_eq_iversion(inode, data->cookie)) {
117 		pr_debug("readdir() left off=%d\n", ino);
118 		goto inside;
119 	}
120 
121 	ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
122 	for (i = 0; ino && i < chain_pos; i++) {
123 		fh_bh = affs_bread(sb, ino);
124 		if (!fh_bh) {
125 			affs_error(sb, "readdir","Cannot read block %d", i);
126 			error = -EIO;
127 			goto out_brelse_dir;
128 		}
129 		ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
130 		affs_brelse(fh_bh);
131 		fh_bh = NULL;
132 	}
133 	if (ino)
134 		goto inside;
135 	hash_pos++;
136 
137 	for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) {
138 		ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
139 		if (!ino)
140 			continue;
141 		ctx->pos = (hash_pos << 16) + 2;
142 inside:
143 		do {
144 			fh_bh = affs_bread(sb, ino);
145 			if (!fh_bh) {
146 				affs_error(sb, "readdir",
147 					   "Cannot read block %d", ino);
148 				break;
149 			}
150 
151 			namelen = min(AFFS_TAIL(sb, fh_bh)->name[0],
152 				      (u8)AFFSNAMEMAX);
153 			name = AFFS_TAIL(sb, fh_bh)->name + 1;
154 			pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n",
155 				 namelen, name, ino, hash_pos, ctx->pos);
156 
157 			if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN))
158 				goto done;
159 			ctx->pos++;
160 			ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
161 			affs_brelse(fh_bh);
162 			fh_bh = NULL;
163 		} while (ino);
164 	}
165 done:
166 	data->cookie = inode_query_iversion(inode);
167 	data->ino = ino;
168 	affs_brelse(fh_bh);
169 
170 out_brelse_dir:
171 	affs_brelse(dir_bh);
172 
173 out_unlock_dir:
174 	affs_unlock_dir(inode);
175 	return error;
176 }
177