blob: 859135bd9660035edc613661ed9b047c5dd91361 [file] [log] [blame]
Theodore Ts'o30fab291997-10-25 22:37:42 +00001/*
2 * fileio.c --- Simple file I/O routines
3 *
4 * 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
Theodore Ts'o30fab291997-10-25 22:37:42 +000017
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000018#include "ext2_fs.h"
Theodore Ts'o30fab291997-10-25 22:37:42 +000019#include "ext2fs.h"
20
21struct ext2_file {
22 errcode_t magic;
23 ext2_filsys fs;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000024 ext2_ino_t ino;
Theodore Ts'o30fab291997-10-25 22:37:42 +000025 struct ext2_inode inode;
26 int flags;
Theodore Ts'o819157d2003-01-22 18:25:39 -050027 __u64 pos;
Theodore Ts'o30fab291997-10-25 22:37:42 +000028 blk_t blockno;
29 blk_t physblock;
30 char *buf;
31};
32
Theodore Ts'o0f679452001-05-05 16:42:24 +000033#define BMAP_BUFFER (file->buf + fs->blocksize)
34
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000035errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
Theodore Ts'o30fab291997-10-25 22:37:42 +000036 int flags, ext2_file_t *ret)
37{
38 ext2_file_t file;
39 errcode_t retval;
40
41 /*
42 * Don't let caller create or open a file for writing if the
43 * filesystem is read-only.
44 */
45 if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
46 !(fs->flags & EXT2_FLAG_RW))
47 return EXT2_ET_RO_FILSYS;
48
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000049 retval = ext2fs_get_mem(sizeof(struct ext2_file), (void **) &file);
50 if (retval)
51 return retval;
Theodore Ts'o30fab291997-10-25 22:37:42 +000052
53 memset(file, 0, sizeof(struct ext2_file));
54 file->magic = EXT2_ET_MAGIC_EXT2_FILE;
55 file->fs = fs;
56 file->ino = ino;
57 file->flags = flags & EXT2_FILE_MASK;
58
59 retval = ext2fs_read_inode(fs, ino, &file->inode);
60 if (retval)
61 goto fail;
62
Theodore Ts'o0f679452001-05-05 16:42:24 +000063 retval = ext2fs_get_mem(fs->blocksize * 3, (void **) &file->buf);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000064 if (retval)
Theodore Ts'o30fab291997-10-25 22:37:42 +000065 goto fail;
Theodore Ts'o30fab291997-10-25 22:37:42 +000066
67 *ret = file;
68 return 0;
69
70fail:
71 if (file->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000072 ext2fs_free_mem((void **) &file->buf);
73 ext2fs_free_mem((void **) &file);
Theodore Ts'o30fab291997-10-25 22:37:42 +000074 return retval;
75}
76
77/*
Theodore Ts'o79a90bd1997-11-03 19:16:55 +000078 * This function returns the filesystem handle of a file from the structure
79 */
80ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
81{
82 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
83 return 0;
84 return file->fs;
85}
86
87/*
Theodore Ts'o30fab291997-10-25 22:37:42 +000088 * This function flushes the dirty block buffer out to disk if
89 * necessary.
90 */
Theodore Ts'of12e2852002-02-20 01:06:25 -050091errcode_t ext2fs_file_flush(ext2_file_t file)
Theodore Ts'o30fab291997-10-25 22:37:42 +000092{
93 errcode_t retval;
Theodore Ts'o0f679452001-05-05 16:42:24 +000094 ext2_filsys fs;
Theodore Ts'o30fab291997-10-25 22:37:42 +000095
96 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
Theodore Ts'o0f679452001-05-05 16:42:24 +000097 fs = file->fs;
Theodore Ts'o30fab291997-10-25 22:37:42 +000098
99 if (!(file->flags & EXT2_FILE_BUF_VALID) ||
100 !(file->flags & EXT2_FILE_BUF_DIRTY))
101 return 0;
102
103 /*
104 * OK, the physical block hasn't been allocated yet.
105 * Allocate it.
106 */
107 if (!file->physblock) {
Theodore Ts'o0f679452001-05-05 16:42:24 +0000108 retval = ext2fs_bmap(fs, file->ino, &file->inode,
109 BMAP_BUFFER, BMAP_ALLOC,
Theodore Ts'o30fab291997-10-25 22:37:42 +0000110 file->blockno, &file->physblock);
111 if (retval)
112 return retval;
113 }
114
Theodore Ts'o0f679452001-05-05 16:42:24 +0000115 retval = io_channel_write_blk(fs->io, file->physblock,
Theodore Ts'o30fab291997-10-25 22:37:42 +0000116 1, file->buf);
117 if (retval)
118 return retval;
119
120 file->flags &= ~EXT2_FILE_BUF_DIRTY;
121
122 return retval;
123}
124
Theodore Ts'o0f679452001-05-05 16:42:24 +0000125/*
126 * This function synchronizes the file's block buffer and the current
127 * file position, possibly invalidating block buffer if necessary
128 */
129static errcode_t sync_buffer_position(ext2_file_t file)
130{
131 blk_t b;
132 errcode_t retval;
133
134 b = file->pos / file->fs->blocksize;
135 if (b != file->blockno) {
136 retval = ext2fs_file_flush(file);
137 if (retval)
138 return retval;
139 file->flags &= ~EXT2_FILE_BUF_VALID;
140 }
141 file->blockno = b;
142 return 0;
143}
144
145/*
146 * This function loads the file's block buffer with valid data from
147 * the disk as necessary.
148 *
149 * If dontfill is true, then skip initializing the buffer since we're
150 * going to be replacing its entire contents anyway. If set, then the
151 * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
152 */
153#define DONTFILL 1
154static errcode_t load_buffer(ext2_file_t file, int dontfill)
155{
156 ext2_filsys fs = file->fs;
157 errcode_t retval;
158
159 if (!(file->flags & EXT2_FILE_BUF_VALID)) {
160 retval = ext2fs_bmap(fs, file->ino, &file->inode,
161 BMAP_BUFFER, 0, file->blockno,
162 &file->physblock);
163 if (retval)
164 return retval;
165 if (!dontfill) {
166 if (file->physblock) {
167 retval = io_channel_read_blk(fs->io,
168 file->physblock,
169 1, file->buf);
170 if (retval)
171 return retval;
172 } else
173 memset(file->buf, 0, fs->blocksize);
174 }
175 file->flags |= EXT2_FILE_BUF_VALID;
176 }
177 return 0;
178}
179
180
Theodore Ts'o30fab291997-10-25 22:37:42 +0000181errcode_t ext2fs_file_close(ext2_file_t file)
182{
183 errcode_t retval;
184
185 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
186
187 retval = ext2fs_file_flush(file);
188
189 if (file->buf)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000190 ext2fs_free_mem((void **) &file->buf);
191 ext2fs_free_mem((void **) &file);
Theodore Ts'o30fab291997-10-25 22:37:42 +0000192
193 return retval;
194}
195
196
197errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000198 unsigned int wanted, unsigned int *got)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000199{
200 ext2_filsys fs;
Theodore Ts'o0f679452001-05-05 16:42:24 +0000201 errcode_t retval = 0;
Theodore Ts'o819157d2003-01-22 18:25:39 -0500202 unsigned int start, c, count = 0;
203 __u64 left;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000204 char *ptr = (char *) buf;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000205
206 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
207 fs = file->fs;
208
Theodore Ts'o819157d2003-01-22 18:25:39 -0500209 while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
Theodore Ts'o0f679452001-05-05 16:42:24 +0000210 retval = sync_buffer_position(file);
211 if (retval)
212 goto fail;
213 retval = load_buffer(file, 0);
214 if (retval)
215 goto fail;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000216
Theodore Ts'o0f679452001-05-05 16:42:24 +0000217 start = file->pos % fs->blocksize;
218 c = fs->blocksize - start;
219 if (c > wanted)
220 c = wanted;
Theodore Ts'o819157d2003-01-22 18:25:39 -0500221 left = EXT2_I_SIZE(&file->inode) - file->pos ;
Theodore Ts'o0f679452001-05-05 16:42:24 +0000222 if (c > left)
223 c = left;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000224
Theodore Ts'o0f679452001-05-05 16:42:24 +0000225 memcpy(ptr, file->buf+start, c);
226 file->pos += c;
227 ptr += c;
228 count += c;
229 wanted -= c;
230 }
231
232fail:
Theodore Ts'o30fab291997-10-25 22:37:42 +0000233 if (got)
234 *got = count;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000235 return retval;
236}
237
238
Theodore Ts'of12e2852002-02-20 01:06:25 -0500239errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000240 unsigned int nbytes, unsigned int *written)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000241{
242 ext2_filsys fs;
Theodore Ts'o0f679452001-05-05 16:42:24 +0000243 errcode_t retval = 0;
244 unsigned int start, c, count = 0;
Theodore Ts'o546a1ff2002-03-07 23:52:56 -0500245 const char *ptr = (const char *) buf;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000246
247 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
248 fs = file->fs;
249
250 if (!(file->flags & EXT2_FILE_WRITE))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000251 return EXT2_ET_FILE_RO;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000252
Theodore Ts'o0f679452001-05-05 16:42:24 +0000253 while (nbytes > 0) {
254 retval = sync_buffer_position(file);
Theodore Ts'o30fab291997-10-25 22:37:42 +0000255 if (retval)
256 goto fail;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000257
Theodore Ts'o0f679452001-05-05 16:42:24 +0000258 start = file->pos % fs->blocksize;
259 c = fs->blocksize - start;
260 if (c > nbytes)
261 c = nbytes;
262
263 /*
264 * We only need to do a read-modify-update cycle if
265 * we're doing a partial write.
266 */
267 retval = load_buffer(file, (c == fs->blocksize));
Theodore Ts'o30fab291997-10-25 22:37:42 +0000268 if (retval)
269 goto fail;
Theodore Ts'o0f679452001-05-05 16:42:24 +0000270
271 file->flags |= EXT2_FILE_BUF_DIRTY;
272 memcpy(file->buf+start, ptr, c);
273 file->pos += c;
274 ptr += c;
275 count += c;
276 nbytes -= c;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000277 }
Theodore Ts'o30fab291997-10-25 22:37:42 +0000278
Theodore Ts'o0f679452001-05-05 16:42:24 +0000279fail:
Theodore Ts'o30fab291997-10-25 22:37:42 +0000280 if (written)
281 *written = count;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000282 return retval;
283}
284
Theodore Ts'o819157d2003-01-22 18:25:39 -0500285errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
286 int whence, __u64 *ret_pos)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000287{
288 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
289
290 if (whence == EXT2_SEEK_SET)
291 file->pos = offset;
292 else if (whence == EXT2_SEEK_CUR)
293 file->pos += offset;
294 else if (whence == EXT2_SEEK_END)
Theodore Ts'o819157d2003-01-22 18:25:39 -0500295 file->pos = EXT2_I_SIZE(&file->inode) + offset;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000296 else
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000297 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000298
299 if (ret_pos)
300 *ret_pos = file->pos;
301
302 return 0;
303}
304
Theodore Ts'o819157d2003-01-22 18:25:39 -0500305errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
306 int whence, ext2_off_t *ret_pos)
307{
308 __u64 loffset, ret_loffset;
309 errcode_t retval;
310
311 loffset = offset;
312 retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
313 *ret_pos = (ext2_off_t) ret_loffset;
314 return retval;
315}
316
317
318/*
319 * This function returns the size of the file, according to the inode
320 */
321errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
322{
323 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
324 return EXT2_ET_MAGIC_EXT2_FILE;
325 *ret_size = EXT2_I_SIZE(&file->inode);
326 return 0;
327}
328
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000329/*
330 * This function returns the size of the file, according to the inode
331 */
332ext2_off_t ext2fs_file_get_size(ext2_file_t file)
333{
Theodore Ts'o819157d2003-01-22 18:25:39 -0500334 __u64 size;
335
336 if (ext2fs_file_get_lsize(file, &size))
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000337 return 0;
Theodore Ts'o819157d2003-01-22 18:25:39 -0500338 if ((size >> 32) != 0)
339 return 0;
340 return size;
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000341}
Theodore Ts'o30fab291997-10-25 22:37:42 +0000342
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000343/*
344 * This function sets the size of the file, truncating it if necessary
345 *
346 * XXX still need to call truncate
347 */
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000348errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000349{
350 errcode_t retval;
351 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
352
353 file->inode.i_size = size;
Theodore Ts'o819157d2003-01-22 18:25:39 -0500354 file->inode.i_size_high = 0;
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000355 retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
356 if (retval)
357 return retval;
358
359 /*
360 * XXX truncate inode if necessary
361 */
362
363 return 0;
364}