[GFS2] Update init_dinode() to reduce stack usage
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / fs / gfs2 / jdata.c
CommitLineData
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
25int 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
62int 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
83int 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
101static 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
134int 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
220int 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
241int 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
255static 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
292int 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