-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile.c
312 lines (261 loc) · 7.63 KB
/
file.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
* Chunkfs file routines
*
* (C) 2007-2008 Valerie Henson <[email protected]>
*/
#include <linux/module.h>
#include <linux/security.h>
#include <linux/quotaops.h>
#include <linux/file.h>
#include "chunkfs.h"
#include "chunkfs_pool.h"
#include "chunkfs_dev.h"
#include "chunkfs_chunk.h"
#include "chunkfs_i.h"
/*
* The point of all these wrapper functions is the following:
*
* We need to get set the right file ops in the file struct for the
* area of the file being altered.
*
* For ops which affect the entire file (like fsync), we need to fan
* out to all the parts of the file.
*
* It would be cool if we could set the file operations depending on
* where in the file the I/O is happening. But I don't think we have
* that ability in the VFS right now.
*/
/*
* Set the client file position to be relative to the start of the
* client file and copy down the main file struct's data in to the
* client file struct.
*/
void
chunkfs_copy_down_file(struct file *file, loff_t *ppos,
struct file *client_file, u64 client_start)
{
client_file->f_pos = *ppos - client_start;
*ppos = client_file->f_pos;
chunkfs_debug("client f_pos set to %llu (parent %llu, client_start %llu)\n",
client_file->f_pos, file->f_pos, client_start);
}
/*
* Aaaand reverse the pos conversion.
*/
static void
copy_up_file(struct file *file, struct file *client_file, u64 client_start)
{
file->f_pos = client_file->f_pos + client_start;
chunkfs_debug("file f_pos set to %llu (client f_pos %llu client_start %llu)\n",
file->f_pos, client_file->f_pos, client_start);
}
/*
* Open the client inode at offset and return the file struct.
*/
int
chunkfs_open_cont_file(struct file *file, loff_t *ppos,
struct file **client_file,
struct chunkfs_continuation **ret_cont)
{
struct chunkfs_continuation *cont;
struct chunkfs_cont_data *cd;
struct file *new_file;
/* TODO: embed struct path into chunkfs_continuation */
struct path co_path;
int err;
chunkfs_debug("pos %llu\n", *ppos);
err = chunkfs_get_cont_at_offset(file->f_dentry, *ppos, &cont);
if (err)
return err;
co_path.mnt = cont->co_mnt;
co_path.dentry = cont->co_dentry;
new_file = dentry_open(&co_path, file->f_flags, file->f_cred);
if (IS_ERR(new_file)) {
err = PTR_ERR(new_file);
chunkfs_debug("dentry_open: err %d\n", err);
goto out;
}
cd = &cont->co_cd;
chunkfs_copy_down_file(file, ppos, new_file, cd->cd_start);
*ret_cont = cont;
*client_file = new_file;
out:
chunkfs_debug("returning %d\n", err);
return err;
}
void
chunkfs_close_cont_file(struct file *file, struct file *client_file,
struct chunkfs_continuation *cont)
{
struct chunkfs_cont_data *cd = &cont->co_cd;
/* XXX... sys_close does a lot more than this. */
chunkfs_debug("enter\n");
copy_up_file(file, client_file, cd->cd_start);
chunkfs_copy_up_inode(file->f_dentry->d_inode,
client_file->f_dentry->d_inode);
chunkfs_put_continuation(cont);
}
/*
* lseek only affects the top-level file struct's fpos.
*/
static loff_t
chunkfs_llseek_file(struct file *file, loff_t offset, int origin)
{
chunkfs_debug("enter\n");
/* XXX right generic llseek? */
return default_llseek(file, offset, origin);
}
/*
* Find the right inode for the offset and read from it. Opens and
* closes the client file struct every time because I'm lazy.
*/
static ssize_t
chunkfs_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
{
struct file *client_file;
struct chunkfs_continuation *cont;
int err;
chunkfs_debug("enter\n");
err = chunkfs_open_cont_file(file, ppos, &client_file, &cont);
/* Read off the end of the file */
/* XXX distinguish between this and EIO */
if (err == -ENOENT)
return 0;
if (err)
return err;
/* XXX assume not longer than len */
if (client_file->f_op->read)
err = client_file->f_op->read(client_file, buf, len, ppos);
else
err = do_sync_read(client_file, buf, len, ppos);
/* If we read off the end, no problemo */
if (err == -ENODATA)
err = 0;
chunkfs_close_cont_file(file, client_file, cont);
return err;
}
static ssize_t
chunkfs_write(struct file *file, const char __user *buf, size_t len,
loff_t *ppos)
{
struct chunkfs_continuation *cont;
struct file *client_file;
ssize_t size;
int err;
chunkfs_debug("pos %llu len %zu\n", *ppos, len);
err = chunkfs_open_cont_file(file, ppos, &client_file, &cont);
if (err == -ENOENT) {
err = chunkfs_create_continuation(file, ppos, &client_file,
&cont);
}
if (err)
return err;
/* XXX assume not longer than len */
if (client_file->f_op->write)
size = client_file->f_op->write(client_file, buf, len, ppos);
else
size = do_sync_write(client_file, buf, len, ppos);
chunkfs_close_cont_file(file, client_file, cont);
chunkfs_debug("pos %llu len %zu, returning size %zu\n", *ppos, len, size);
return size;
}
/*
* Open only affects the top-level chunkfs file struct. Do an open of
* the underlying head client inode just to see that we can, then
* close it again.
*/
int
chunkfs_open(struct inode *inode, struct file *filp)
{
struct file *client_file;
struct chunkfs_continuation *cont;
loff_t dummy_pos = 0;
int err;
chunkfs_debug("enter\n");
err = chunkfs_open_cont_file(filp, &dummy_pos, &client_file, &cont);
if (err)
goto out;
chunkfs_close_cont_file(filp, client_file, cont);
return 0;
out:
chunkfs_debug("returning %d\n", err);
return err;
}
/*
* Apparently, file may be null at this point. Uh. Whatever.
*/
static int
chunkfs_fsync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
struct chunkfs_inode_info *ii = CHUNKFS_I(file->f_dentry->d_inode);
struct chunkfs_continuation *prev_cont = NULL;
struct chunkfs_continuation *next_cont;
struct dentry *client_dentry;
struct inode *client_inode;
struct file client_file;
int err = -EIO;
chunkfs_debug("enter\n");
/* XXX syncs all inodes instead of just ones in mem */
mutex_lock(&ii->ii_continuations_lock);
while (1) {
err = chunkfs_get_next_cont(file->f_dentry, prev_cont, &next_cont);
if (err || (next_cont == NULL))
break;
client_dentry = next_cont->co_dentry;
client_inode = client_dentry->d_inode;
client_file.f_dentry = client_dentry;
client_file.f_inode = client_inode;
/* XXX error propagation */
err = client_inode->i_fop->fsync(&client_file, start, end, datasync);
prev_cont = next_cont;
}
mutex_unlock(&ii->ii_continuations_lock);
chunkfs_debug("err %d\n", err);
return err;
}
int chunkfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *client_inode = get_client_inode(dentry->d_inode);
struct dentry *client_dentry = get_client_dentry(dentry);
int error;
chunkfs_debug("enter\n");
if (client_inode->i_op->setattr) {
error = client_inode->i_op->setattr(client_dentry, attr);
} else {
/* Arrrrrgh gross argh */
error = inode_change_ok(client_inode, attr);
if (!error)
error = security_inode_setattr(client_dentry, attr);
if (!error) {
setattr_copy(client_inode, attr);
mark_inode_dirty(client_inode);
}
}
if (!error)
chunkfs_copy_up_inode(dentry->d_inode, client_inode);
return error;
}
/*
* XXX probably need to change the nd, that was here before
*/
int chunkfs_permission(struct inode *inode, int submask)
{
struct inode *client_inode = get_client_inode(inode);
int err;
if (client_inode->i_op->permission)
err = client_inode->i_op->permission(client_inode, submask);
else
err = generic_permission(client_inode, submask);
return err;
}
struct file_operations chunkfs_file_fops = {
.llseek = chunkfs_llseek_file,
.read = chunkfs_read,
.write = chunkfs_write,
.open = chunkfs_open,
.fsync = chunkfs_fsync_file,
};
struct inode_operations chunkfs_file_iops = {
.setattr = chunkfs_setattr,
.permission = chunkfs_permission,
};