Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/affs/dir.c
26278 views
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
178