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