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 | ||
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 |