diff options
Diffstat (limited to 'fs/gfs2/jdata.c')
-rw-r--r-- | fs/gfs2/jdata.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/fs/gfs2/jdata.c b/fs/gfs2/jdata.c new file mode 100644 index 000000000000..d4adbf171ed3 --- /dev/null +++ b/fs/gfs2/jdata.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. | ||
3 | * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. | ||
4 | * | ||
5 | * This copyrighted material is made available to anyone wishing to use, | ||
6 | * modify, copy, or redistribute it subject to the terms and conditions | ||
7 | * of the GNU General Public License v.2. | ||
8 | */ | ||
9 | |||
10 | #include <linux/sched.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/spinlock.h> | ||
13 | #include <linux/completion.h> | ||
14 | #include <linux/buffer_head.h> | ||
15 | #include <asm/semaphore.h> | ||
16 | #include <asm/uaccess.h> | ||
17 | |||
18 | #include "gfs2.h" | ||
19 | #include "bmap.h" | ||
20 | #include "inode.h" | ||
21 | #include "jdata.h" | ||
22 | #include "meta_io.h" | ||
23 | #include "trans.h" | ||
24 | |||
25 | int gfs2_jdata_get_buffer(struct gfs2_inode *ip, uint64_t block, int new, | ||
26 | struct buffer_head **bhp) | ||
27 | { | ||
28 | struct buffer_head *bh; | ||
29 | int error = 0; | ||
30 | |||
31 | if (new) { | ||
32 | bh = gfs2_meta_new(ip->i_gl, block); | ||
33 | gfs2_trans_add_bh(ip->i_gl, bh); | ||
34 | gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD); | ||
35 | gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header)); | ||
36 | } else { | ||
37 | error = gfs2_meta_read(ip->i_gl, block, | ||
38 | DIO_START | DIO_WAIT, &bh); | ||
39 | if (error) | ||
40 | return error; | ||
41 | if (gfs2_metatype_check(ip->i_sbd, bh, GFS2_METATYPE_JD)) { | ||
42 | brelse(bh); | ||
43 | return -EIO; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | *bhp = bh; | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * gfs2_copy2mem - Trivial copy function for gfs2_jdata_read() | ||
54 | * @bh: The buffer to copy from, or NULL meaning zero the buffer | ||
55 | * @buf: The buffer to copy/zero | ||
56 | * @offset: The offset in the buffer to copy from | ||
57 | * @size: The amount of data to copy/zero | ||
58 | * | ||
59 | * Returns: errno | ||
60 | */ | ||
61 | |||
62 | int gfs2_copy2mem(struct buffer_head *bh, char **buf, unsigned int offset, | ||
63 | unsigned int size) | ||
64 | { | ||
65 | if (bh) | ||
66 | memcpy(*buf, bh->b_data + offset, size); | ||
67 | else | ||
68 | memset(*buf, 0, size); | ||
69 | *buf += size; | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * gfs2_copy2user - Copy bytes to user space for gfs2_jdata_read() | ||
75 | * @bh: The buffer | ||
76 | * @buf: The destination of the data | ||
77 | * @offset: The offset into the buffer | ||
78 | * @size: The amount of data to copy | ||
79 | * | ||
80 | * Returns: errno | ||
81 | */ | ||
82 | |||
83 | int gfs2_copy2user(struct buffer_head *bh, char **buf, unsigned int offset, | ||
84 | unsigned int size) | ||
85 | { | ||
86 | int error; | ||
87 | |||
88 | if (bh) | ||
89 | error = copy_to_user(*buf, bh->b_data + offset, size); | ||
90 | else | ||
91 | error = clear_user(*buf, size); | ||
92 | |||
93 | if (error) | ||
94 | error = -EFAULT; | ||
95 | else | ||
96 | *buf += size; | ||
97 | |||
98 | return error; | ||
99 | } | ||
100 | |||
101 | static int jdata_read_stuffed(struct gfs2_inode *ip, char *buf, | ||
102 | unsigned int offset, unsigned int size, | ||
103 | read_copy_fn_t copy_fn) | ||
104 | { | ||
105 | struct buffer_head *dibh; | ||
106 | int error; | ||
107 | |||
108 | error = gfs2_meta_inode_buffer(ip, &dibh); | ||
109 | if (!error) { | ||
110 | error = copy_fn(dibh, &buf, | ||
111 | offset + sizeof(struct gfs2_dinode), size); | ||
112 | brelse(dibh); | ||
113 | } | ||
114 | |||
115 | return (error) ? error : size; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * gfs2_jdata_read - Read a jdata file | ||
120 | * @ip: The GFS2 Inode | ||
121 | * @buf: The buffer to place result into | ||
122 | * @offset: File offset to begin jdata_readng from | ||
123 | * @size: Amount of data to transfer | ||
124 | * @copy_fn: Function to actually perform the copy | ||
125 | * | ||
126 | * The @copy_fn only copies a maximum of a single block at once so | ||
127 | * we are safe calling it with int arguments. It is done so that | ||
128 | * we don't needlessly put 64bit arguments on the stack and it | ||
129 | * also makes the code in the @copy_fn nicer too. | ||
130 | * | ||
131 | * Returns: The amount of data actually copied or the error | ||
132 | */ | ||
133 | |||
134 | int gfs2_jdata_read(struct gfs2_inode *ip, char __user *buf, uint64_t offset, | ||
135 | unsigned int size, read_copy_fn_t copy_fn) | ||
136 | { | ||
137 | struct gfs2_sbd *sdp = ip->i_sbd; | ||
138 | uint64_t lblock, dblock; | ||
139 | uint32_t extlen = 0; | ||
140 | unsigned int o; | ||
141 | int copied = 0; | ||
142 | int error = 0; | ||
143 | |||
144 | if (offset >= ip->i_di.di_size) | ||
145 | return 0; | ||
146 | |||
147 | if ((offset + size) > ip->i_di.di_size) | ||
148 | size = ip->i_di.di_size - offset; | ||
149 | |||
150 | if (!size) | ||
151 | return 0; | ||
152 | |||
153 | if (gfs2_is_stuffed(ip)) | ||
154 | return jdata_read_stuffed(ip, buf, (unsigned int)offset, size, | ||
155 | copy_fn); | ||
156 | |||
157 | if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip))) | ||
158 | return -EINVAL; | ||
159 | |||
160 | lblock = offset; | ||
161 | o = do_div(lblock, sdp->sd_jbsize) + | ||
162 | sizeof(struct gfs2_meta_header); | ||
163 | |||
164 | while (copied < size) { | ||
165 | unsigned int amount; | ||
166 | struct buffer_head *bh; | ||
167 | int new; | ||
168 | |||
169 | amount = size - copied; | ||
170 | if (amount > sdp->sd_sb.sb_bsize - o) | ||
171 | amount = sdp->sd_sb.sb_bsize - o; | ||
172 | |||
173 | if (!extlen) { | ||
174 | new = 0; | ||
175 | error = gfs2_block_map(ip, lblock, &new, | ||
176 | &dblock, &extlen); | ||
177 | if (error) | ||
178 | goto fail; | ||
179 | } | ||
180 | |||
181 | if (extlen > 1) | ||
182 | gfs2_meta_ra(ip->i_gl, dblock, extlen); | ||
183 | |||
184 | if (dblock) { | ||
185 | error = gfs2_jdata_get_buffer(ip, dblock, new, &bh); | ||
186 | if (error) | ||
187 | goto fail; | ||
188 | dblock++; | ||
189 | extlen--; | ||
190 | } else | ||
191 | bh = NULL; | ||
192 | |||
193 | error = copy_fn(bh, &buf, o, amount); | ||
194 | brelse(bh); | ||
195 | if (error) | ||
196 | goto fail; | ||
197 | |||
198 | copied += amount; | ||
199 | lblock++; | ||
200 | |||
201 | o = sizeof(struct gfs2_meta_header); | ||
202 | } | ||
203 | |||
204 | return copied; | ||
205 | |||
206 | fail: | ||
207 | return (copied) ? copied : error; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * gfs2_copy_from_mem - Trivial copy function for gfs2_jdata_write() | ||
212 | * @bh: The buffer to copy to or clear | ||
213 | * @buf: The buffer to copy from | ||
214 | * @offset: The offset in the buffer to write to | ||
215 | * @size: The amount of data to write | ||
216 | * | ||
217 | * Returns: errno | ||
218 | */ | ||
219 | |||
220 | int gfs2_copy_from_mem(struct gfs2_inode *ip, struct buffer_head *bh, | ||
221 | const char **buf, unsigned int offset, unsigned int size) | ||
222 | { | ||
223 | gfs2_trans_add_bh(ip->i_gl, bh); | ||
224 | memcpy(bh->b_data + offset, *buf, size); | ||
225 | |||
226 | *buf += size; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * gfs2_copy_from_user - Copy bytes from user space for gfs2_jdata_write() | ||
233 | * @bh: The buffer to copy to or clear | ||
234 | * @buf: The buffer to copy from | ||
235 | * @offset: The offset in the buffer to write to | ||
236 | * @size: The amount of data to write | ||
237 | * | ||
238 | * Returns: errno | ||
239 | */ | ||
240 | |||
241 | int gfs2_copy_from_user(struct gfs2_inode *ip, struct buffer_head *bh, | ||
242 | const char __user **buf, unsigned int offset, unsigned int size) | ||
243 | { | ||
244 | int error = 0; | ||
245 | |||
246 | gfs2_trans_add_bh(ip->i_gl, bh); | ||
247 | if (copy_from_user(bh->b_data + offset, *buf, size)) | ||
248 | error = -EFAULT; | ||
249 | else | ||
250 | *buf += size; | ||
251 | |||
252 | return error; | ||
253 | } | ||
254 | |||
255 | static int jdata_write_stuffed(struct gfs2_inode *ip, char *buf, | ||
256 | unsigned int offset, unsigned int size, | ||
257 | write_copy_fn_t copy_fn) | ||
258 | { | ||
259 | struct buffer_head *dibh; | ||
260 | int error; | ||
261 | |||
262 | error = gfs2_meta_inode_buffer(ip, &dibh); | ||
263 | if (error) | ||
264 | return error; | ||
265 | |||
266 | error = copy_fn(ip, | ||
267 | dibh, &buf, | ||
268 | offset + sizeof(struct gfs2_dinode), size); | ||
269 | if (!error) { | ||
270 | if (ip->i_di.di_size < offset + size) | ||
271 | ip->i_di.di_size = offset + size; | ||
272 | ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); | ||
273 | gfs2_dinode_out(&ip->i_di, dibh->b_data); | ||
274 | } | ||
275 | |||
276 | brelse(dibh); | ||
277 | |||
278 | return (error) ? error : size; | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * gfs2_jdata_write - Write bytes to a file | ||
283 | * @ip: The GFS2 inode | ||
284 | * @buf: The buffer containing information to be written | ||
285 | * @offset: The file offset to start writing at | ||
286 | * @size: The amount of data to write | ||
287 | * @copy_fn: Function to do the actual copying | ||
288 | * | ||
289 | * Returns: The number of bytes correctly written or error code | ||
290 | */ | ||
291 | |||
292 | int gfs2_jdata_write(struct gfs2_inode *ip, const char __user *buf, uint64_t offset, | ||
293 | unsigned int size, write_copy_fn_t copy_fn) | ||
294 | { | ||
295 | struct gfs2_sbd *sdp = ip->i_sbd; | ||
296 | struct buffer_head *dibh; | ||
297 | uint64_t lblock, dblock; | ||
298 | uint32_t extlen = 0; | ||
299 | unsigned int o; | ||
300 | int copied = 0; | ||
301 | int error = 0; | ||
302 | |||
303 | if (!size) | ||
304 | return 0; | ||
305 | |||
306 | if (gfs2_is_stuffed(ip) && | ||
307 | offset + size <= sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) | ||
308 | return jdata_write_stuffed(ip, buf, (unsigned int)offset, size, | ||
309 | copy_fn); | ||
310 | |||
311 | if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip))) | ||
312 | return -EINVAL; | ||
313 | |||
314 | if (gfs2_is_stuffed(ip)) { | ||
315 | error = gfs2_unstuff_dinode(ip, NULL, NULL); | ||
316 | if (error) | ||
317 | return error; | ||
318 | } | ||
319 | |||
320 | lblock = offset; | ||
321 | o = do_div(lblock, sdp->sd_jbsize) + sizeof(struct gfs2_meta_header); | ||
322 | |||
323 | while (copied < size) { | ||
324 | unsigned int amount; | ||
325 | struct buffer_head *bh; | ||
326 | int new; | ||
327 | |||
328 | amount = size - copied; | ||
329 | if (amount > sdp->sd_sb.sb_bsize - o) | ||
330 | amount = sdp->sd_sb.sb_bsize - o; | ||
331 | |||
332 | if (!extlen) { | ||
333 | new = 1; | ||
334 | error = gfs2_block_map(ip, lblock, &new, | ||
335 | &dblock, &extlen); | ||
336 | if (error) | ||
337 | goto fail; | ||
338 | error = -EIO; | ||
339 | if (gfs2_assert_withdraw(sdp, dblock)) | ||
340 | goto fail; | ||
341 | } | ||
342 | |||
343 | error = gfs2_jdata_get_buffer(ip, dblock, | ||
344 | (amount == sdp->sd_jbsize) ? 1 : new, | ||
345 | &bh); | ||
346 | if (error) | ||
347 | goto fail; | ||
348 | |||
349 | error = copy_fn(ip, bh, &buf, o, amount); | ||
350 | brelse(bh); | ||
351 | if (error) | ||
352 | goto fail; | ||
353 | |||
354 | copied += amount; | ||
355 | lblock++; | ||
356 | dblock++; | ||
357 | extlen--; | ||
358 | |||
359 | o = sizeof(struct gfs2_meta_header); | ||
360 | } | ||
361 | |||
362 | out: | ||
363 | error = gfs2_meta_inode_buffer(ip, &dibh); | ||
364 | if (error) | ||
365 | return error; | ||
366 | |||
367 | if (ip->i_di.di_size < offset + copied) | ||
368 | ip->i_di.di_size = offset + copied; | ||
369 | ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); | ||
370 | |||
371 | gfs2_trans_add_bh(ip->i_gl, dibh); | ||
372 | gfs2_dinode_out(&ip->i_di, dibh->b_data); | ||
373 | brelse(dibh); | ||
374 | |||
375 | return copied; | ||
376 | |||
377 | fail: | ||
378 | if (copied) | ||
379 | goto out; | ||
380 | return error; | ||
381 | } | ||
382 | |||