blob: 32f5b37273a49dbabdf8dc82fd06d95a52d69e59 [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;
27 ext2_off_t pos;
28 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'o79a90bd1997-11-03 19:16:55 +0000202 unsigned int start, left, c, count = 0;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000203 char *ptr = (char *) buf;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000204
205 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
206 fs = file->fs;
207
Theodore Ts'o0f679452001-05-05 16:42:24 +0000208 while ((file->pos < file->inode.i_size) && (wanted > 0)) {
209 retval = sync_buffer_position(file);
210 if (retval)
211 goto fail;
212 retval = load_buffer(file, 0);
213 if (retval)
214 goto fail;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000215
Theodore Ts'o0f679452001-05-05 16:42:24 +0000216 start = file->pos % fs->blocksize;
217 c = fs->blocksize - start;
218 if (c > wanted)
219 c = wanted;
220 left = file->inode.i_size - file->pos ;
221 if (c > left)
222 c = left;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000223
Theodore Ts'o0f679452001-05-05 16:42:24 +0000224 memcpy(ptr, file->buf+start, c);
225 file->pos += c;
226 ptr += c;
227 count += c;
228 wanted -= c;
229 }
230
231fail:
Theodore Ts'o30fab291997-10-25 22:37:42 +0000232 if (got)
233 *got = count;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000234 return retval;
235}
236
237
Theodore Ts'of12e2852002-02-20 01:06:25 -0500238errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000239 unsigned int nbytes, unsigned int *written)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000240{
241 ext2_filsys fs;
Theodore Ts'o0f679452001-05-05 16:42:24 +0000242 errcode_t retval = 0;
243 unsigned int start, c, count = 0;
Theodore Ts'o546a1ff2002-03-07 23:52:56 -0500244 const char *ptr = (const char *) buf;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000245
246 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
247 fs = file->fs;
248
249 if (!(file->flags & EXT2_FILE_WRITE))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000250 return EXT2_ET_FILE_RO;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000251
Theodore Ts'o0f679452001-05-05 16:42:24 +0000252 while (nbytes > 0) {
253 retval = sync_buffer_position(file);
Theodore Ts'o30fab291997-10-25 22:37:42 +0000254 if (retval)
255 goto fail;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000256
Theodore Ts'o0f679452001-05-05 16:42:24 +0000257 start = file->pos % fs->blocksize;
258 c = fs->blocksize - start;
259 if (c > nbytes)
260 c = nbytes;
261
262 /*
263 * We only need to do a read-modify-update cycle if
264 * we're doing a partial write.
265 */
266 retval = load_buffer(file, (c == fs->blocksize));
Theodore Ts'o30fab291997-10-25 22:37:42 +0000267 if (retval)
268 goto fail;
Theodore Ts'o0f679452001-05-05 16:42:24 +0000269
270 file->flags |= EXT2_FILE_BUF_DIRTY;
271 memcpy(file->buf+start, ptr, c);
272 file->pos += c;
273 ptr += c;
274 count += c;
275 nbytes -= c;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000276 }
Theodore Ts'o30fab291997-10-25 22:37:42 +0000277
Theodore Ts'o0f679452001-05-05 16:42:24 +0000278fail:
Theodore Ts'o30fab291997-10-25 22:37:42 +0000279 if (written)
280 *written = count;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000281 return retval;
282}
283
Theodore Ts'o674a4ee1998-03-23 02:06:52 +0000284errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
285 int whence, ext2_off_t *ret_pos)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000286{
287 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
288
289 if (whence == EXT2_SEEK_SET)
290 file->pos = offset;
291 else if (whence == EXT2_SEEK_CUR)
292 file->pos += offset;
293 else if (whence == EXT2_SEEK_END)
294 file->pos = file->inode.i_size + offset;
295 else
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000296 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000297
298 if (ret_pos)
299 *ret_pos = file->pos;
300
301 return 0;
302}
303
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000304/*
305 * This function returns the size of the file, according to the inode
306 */
307ext2_off_t ext2fs_file_get_size(ext2_file_t file)
308{
309 if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
310 return 0;
311 return file->inode.i_size;
312}
Theodore Ts'o30fab291997-10-25 22:37:42 +0000313
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000314/*
315 * This function sets the size of the file, truncating it if necessary
316 *
317 * XXX still need to call truncate
318 */
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000319errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
Theodore Ts'o79a90bd1997-11-03 19:16:55 +0000320{
321 errcode_t retval;
322 EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
323
324 file->inode.i_size = size;
325 retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
326 if (retval)
327 return retval;
328
329 /*
330 * XXX truncate inode if necessary
331 */
332
333 return 0;
334}