blob: da349fea275be31134b30256199f6a02020e476d [file] [log] [blame]
Mike Frysinger1fd98e02005-05-09 22:10:42 +00001/*
2 * fileio.c --- Simple file I/O routines
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003 *
Mike Frysinger1fd98e02005-05-09 22:10:42 +00004 * Copyright (C) 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdio.h>
13#include <string.h>
14#if HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "ext2_fs.h"
19#include "ext2fs.h"
20
21struct ext2_file {
22 errcode_t magic;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000023 ext2_filsys fs;
Mike Frysinger1fd98e02005-05-09 22:10:42 +000024 ext2_ino_t ino;
25 struct ext2_inode inode;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000026 int flags;
Mike Frysinger1fd98e02005-05-09 22:10:42 +000027 __u64 pos;
28 blk_t blockno;
29 blk_t physblock;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000030 char *buf;
Mike Frysinger1fd98e02005-05-09 22:10:42 +000031};
32
33#define BMAP_BUFFER (file->buf + fs->blocksize)
34
35errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
36 struct ext2_inode *inode,
37 int flags, ext2_file_t *ret)
38{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000039 ext2_file_t file;
Mike Frysinger1fd98e02005-05-09 22:10:42 +000040 errcode_t retval;
41
42 /*
43 * Don't let caller create or open a file for writing if the
44 * filesystem is read-only.
45 */
46 if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
47 !(fs->flags & EXT2_FLAG_RW))
48 return EXT2_ET_RO_FILSYS;
49
50 retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
51 if (retval)
52 return retval;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000053
Mike Frysinger1fd98e02005-05-09 22:10:42 +000054 memset(file, 0, sizeof(struct ext2_file));
55 file->magic = EXT2_ET_MAGIC_EXT2_FILE;
56 file->fs = fs;
57 file->ino = ino;
58 file->flags = flags & EXT2_FILE_MASK;
59
60 if (inode) {
61 memcpy(&file->inode, inode, sizeof(struct ext2_inode));
62 } else {
63 retval = ext2fs_read_inode(fs, ino, &file->inode);
64 if (retval)
65 goto fail;
66 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000067
Mike Frysinger1fd98e02005-05-09 22:10:42 +000068 retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
69 if (retval)
70 goto fail;
71
72 *ret = file;
73 return 0;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000074
Mike Frysinger1fd98e02005-05-09 22:10:42 +000075fail:
Rob Landleye7c43b62006-03-01 16:39:45 +000076 ext2fs_free_mem(&file->buf);
Mike Frysinger1fd98e02005-05-09 22:10:42 +000077 ext2fs_free_mem(&file);
78 return retval;
79}
80
81errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
82 int flags, ext2_file_t *ret)
83{
84 return ext2fs_file_open2(fs, ino, NULL, flags, ret);
85}
86
87/*
88 * This function returns the filesystem handle of a file from the structure
89 */
90ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
91{
92 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
93 return 0;
94 return file->fs;
95}
96
97/*
98 * This function flushes the dirty block buffer out to disk if
99 * necessary.
100 */
101errcode_t ext2fs_file_flush(ext2_file_t file)
102{
103 errcode_t retval;
104 ext2_filsys fs;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000105
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000106 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
107 fs = file->fs;
108
109 if (!(file->flags & EXT2_FILE_BUF_VALID) ||
110 !(file->flags & EXT2_FILE_BUF_DIRTY))
111 return 0;
112
113 /*
114 * OK, the physical block hasn't been allocated yet.
115 * Allocate it.
116 */
117 if (!file->physblock) {
118 retval = ext2fs_bmap(fs, file->ino, &file->inode,
119 BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
120 file->blockno, &file->physblock);
121 if (retval)
122 return retval;
123 }
124
125 retval = io_channel_write_blk(fs->io, file->physblock,
126 1, file->buf);
127 if (retval)
128 return retval;
129
130 file->flags &= ~EXT2_FILE_BUF_DIRTY;
131
132 return retval;
133}
134
135/*
136 * This function synchronizes the file's block buffer and the current
137 * file position, possibly invalidating block buffer if necessary
138 */
139static errcode_t sync_buffer_position(ext2_file_t file)
140{
141 blk_t b;
142 errcode_t retval;
143
144 b = file->pos / file->fs->blocksize;
145 if (b != file->blockno) {
146 retval = ext2fs_file_flush(file);
147 if (retval)
148 return retval;
149 file->flags &= ~EXT2_FILE_BUF_VALID;
150 }
151 file->blockno = b;
152 return 0;
153}
154
155/*
156 * This function loads the file's block buffer with valid data from
157 * the disk as necessary.
158 *
159 * If dontfill is true, then skip initializing the buffer since we're
160 * going to be replacing its entire contents anyway. If set, then the
161 * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
162 */
163#define DONTFILL 1
164static errcode_t load_buffer(ext2_file_t file, int dontfill)
165{
166 ext2_filsys fs = file->fs;
167 errcode_t retval;
168
169 if (!(file->flags & EXT2_FILE_BUF_VALID)) {
170 retval = ext2fs_bmap(fs, file->ino, &file->inode,
171 BMAP_BUFFER, 0, file->blockno,
172 &file->physblock);
173 if (retval)
174 return retval;
175 if (!dontfill) {
176 if (file->physblock) {
177 retval = io_channel_read_blk(fs->io,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000178 file->physblock,
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000179 1, file->buf);
180 if (retval)
181 return retval;
182 } else
183 memset(file->buf, 0, fs->blocksize);
184 }
185 file->flags |= EXT2_FILE_BUF_VALID;
186 }
187 return 0;
188}
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000189
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000190
191errcode_t ext2fs_file_close(ext2_file_t file)
192{
193 errcode_t retval;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000194
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000195 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
196
197 retval = ext2fs_file_flush(file);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000198
Rob Landleye7c43b62006-03-01 16:39:45 +0000199 ext2fs_free_mem(&file->buf);
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000200 ext2fs_free_mem(&file);
201
202 return retval;
203}
204
205
206errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
207 unsigned int wanted, unsigned int *got)
208{
209 ext2_filsys fs;
210 errcode_t retval = 0;
211 unsigned int start, c, count = 0;
212 __u64 left;
213 char *ptr = (char *) buf;
214
215 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
216 fs = file->fs;
217
218 while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
219 retval = sync_buffer_position(file);
220 if (retval)
221 goto fail;
222 retval = load_buffer(file, 0);
223 if (retval)
224 goto fail;
225
226 start = file->pos % fs->blocksize;
227 c = fs->blocksize - start;
228 if (c > wanted)
229 c = wanted;
230 left = EXT2_I_SIZE(&file->inode) - file->pos ;
231 if (c > left)
232 c = left;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000233
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000234 memcpy(ptr, file->buf+start, c);
235 file->pos += c;
236 ptr += c;
237 count += c;
238 wanted -= c;
239 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000240
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000241fail:
242 if (got)
243 *got = count;
244 return retval;
245}
246
247
248errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
249 unsigned int nbytes, unsigned int *written)
250{
251 ext2_filsys fs;
252 errcode_t retval = 0;
253 unsigned int start, c, count = 0;
254 const char *ptr = (const char *) buf;
255
256 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
257 fs = file->fs;
258
259 if (!(file->flags & EXT2_FILE_WRITE))
260 return EXT2_ET_FILE_RO;
261
262 while (nbytes > 0) {
263 retval = sync_buffer_position(file);
264 if (retval)
265 goto fail;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000266
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000267 start = file->pos % fs->blocksize;
268 c = fs->blocksize - start;
269 if (c > nbytes)
270 c = nbytes;
271
272 /*
273 * We only need to do a read-modify-update cycle if
274 * we're doing a partial write.
275 */
276 retval = load_buffer(file, (c == fs->blocksize));
277 if (retval)
278 goto fail;
279
280 file->flags |= EXT2_FILE_BUF_DIRTY;
281 memcpy(file->buf+start, ptr, c);
282 file->pos += c;
283 ptr += c;
284 count += c;
285 nbytes -= c;
286 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000287
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000288fail:
289 if (written)
290 *written = count;
291 return retval;
292}
293
294errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
295 int whence, __u64 *ret_pos)
296{
297 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
298
299 if (whence == EXT2_SEEK_SET)
300 file->pos = offset;
301 else if (whence == EXT2_SEEK_CUR)
302 file->pos += offset;
303 else if (whence == EXT2_SEEK_END)
304 file->pos = EXT2_I_SIZE(&file->inode) + offset;
305 else
306 return EXT2_ET_INVALID_ARGUMENT;
307
308 if (ret_pos)
309 *ret_pos = file->pos;
310
311 return 0;
312}
313
314errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
315 int whence, ext2_off_t *ret_pos)
316{
317 __u64 loffset, ret_loffset;
318 errcode_t retval;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000319
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000320 loffset = offset;
321 retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
322 if (ret_pos)
323 *ret_pos = (ext2_off_t) ret_loffset;
324 return retval;
325}
326
327
328/*
329 * This function returns the size of the file, according to the inode
330 */
331errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
332{
333 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
334 return EXT2_ET_MAGIC_EXT2_FILE;
335 *ret_size = EXT2_I_SIZE(&file->inode);
336 return 0;
337}
338
339/*
340 * This function returns the size of the file, according to the inode
341 */
342ext2_off_t ext2fs_file_get_size(ext2_file_t file)
343{
344 __u64 size;
345
346 if (ext2fs_file_get_lsize(file, &size))
347 return 0;
348 if ((size >> 32) != 0)
349 return 0;
350 return size;
351}
352
353/*
354 * This function sets the size of the file, truncating it if necessary
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000355 *
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000356 * XXX still need to call truncate
357 */
358errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
359{
360 errcode_t retval;
361 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000362
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000363 file->inode.i_size = size;
364 file->inode.i_size_high = 0;
365 if (file->ino) {
366 retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
367 if (retval)
368 return retval;
369 }
370
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000371 /*
Mike Frysinger1fd98e02005-05-09 22:10:42 +0000372 * XXX truncate inode if necessary
373 */
374
375 return 0;
376}