Whamcloud - gitweb
ChangeLog, fileio.c:
[tools/e2fsprogs.git] / lib / ext2fs / fileio.c
1 /*
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
17
18 #if EXT2_FLAT_INCLUDES
19 #include "ext2_fs.h"
20 #else
21 #include <linux/ext2_fs.h>
22 #endif
23
24 #include "ext2fs.h"
25
26 struct ext2_file {
27         errcode_t               magic;
28         ext2_filsys             fs;
29         ext2_ino_t              ino;
30         struct ext2_inode       inode;
31         int                     flags;
32         ext2_off_t              pos;
33         blk_t                   blockno;
34         blk_t                   physblock;
35         char                    *buf;
36 };
37
38 #define BMAP_BUFFER (file->buf + fs->blocksize)
39
40 errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
41                            int flags, ext2_file_t *ret)
42 {
43         ext2_file_t     file;
44         errcode_t       retval;
45
46         /*
47          * Don't let caller create or open a file for writing if the
48          * filesystem is read-only.
49          */
50         if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
51             !(fs->flags & EXT2_FLAG_RW))
52                 return EXT2_ET_RO_FILSYS;
53
54         retval = ext2fs_get_mem(sizeof(struct ext2_file), (void **) &file);
55         if (retval)
56                 return retval;
57         
58         memset(file, 0, sizeof(struct ext2_file));
59         file->magic = EXT2_ET_MAGIC_EXT2_FILE;
60         file->fs = fs;
61         file->ino = ino;
62         file->flags = flags & EXT2_FILE_MASK;
63
64         retval = ext2fs_read_inode(fs, ino, &file->inode);
65         if (retval)
66                 goto fail;
67         
68         retval = ext2fs_get_mem(fs->blocksize * 3, (void **) &file->buf);
69         if (retval)
70                 goto fail;
71
72         *ret = file;
73         return 0;
74         
75 fail:
76         if (file->buf)
77                 ext2fs_free_mem((void **) &file->buf);
78         ext2fs_free_mem((void **) &file);
79         return retval;
80 }
81
82 /*
83  * This function returns the filesystem handle of a file from the structure
84  */
85 ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
86 {
87         if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
88                 return 0;
89         return file->fs;
90 }
91
92 /*
93  * This function flushes the dirty block buffer out to disk if
94  * necessary.
95  */
96 static errcode_t ext2fs_file_flush(ext2_file_t file)
97 {
98         errcode_t       retval;
99         ext2_filsys fs;
100         
101         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
102         fs = file->fs;
103
104         if (!(file->flags & EXT2_FILE_BUF_VALID) ||
105             !(file->flags & EXT2_FILE_BUF_DIRTY))
106                 return 0;
107
108         /*
109          * OK, the physical block hasn't been allocated yet.
110          * Allocate it.
111          */
112         if (!file->physblock) {
113                 retval = ext2fs_bmap(fs, file->ino, &file->inode,
114                                      BMAP_BUFFER, BMAP_ALLOC,
115                                      file->blockno, &file->physblock);
116                 if (retval)
117                         return retval;
118         }
119
120         retval = io_channel_write_blk(fs->io, file->physblock,
121                                       1, file->buf);
122         if (retval)
123                 return retval;
124
125         file->flags &= ~EXT2_FILE_BUF_DIRTY;
126
127         return retval;
128 }
129
130 /*
131  * This function synchronizes the file's block buffer and the current
132  * file position, possibly invalidating block buffer if necessary
133  */
134 static errcode_t sync_buffer_position(ext2_file_t file)
135 {
136         blk_t   b;
137         errcode_t       retval;
138
139         b = file->pos / file->fs->blocksize;
140         if (b != file->blockno) {
141                 retval = ext2fs_file_flush(file);
142                 if (retval)
143                         return retval;
144                 file->flags &= ~EXT2_FILE_BUF_VALID;
145         }
146         file->blockno = b;
147         return 0;
148 }
149
150 /*
151  * This function loads the file's block buffer with valid data from
152  * the disk as necessary.
153  *
154  * If dontfill is true, then skip initializing the buffer since we're
155  * going to be replacing its entire contents anyway.  If set, then the
156  * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
157  */
158 #define DONTFILL 1
159 static errcode_t load_buffer(ext2_file_t file, int dontfill)
160 {
161         ext2_filsys     fs = file->fs;
162         errcode_t       retval;
163
164         if (!(file->flags & EXT2_FILE_BUF_VALID)) {
165                 retval = ext2fs_bmap(fs, file->ino, &file->inode,
166                                      BMAP_BUFFER, 0, file->blockno,
167                                      &file->physblock);
168                 if (retval)
169                         return retval;
170                 if (!dontfill) {
171                         if (file->physblock) {
172                                 retval = io_channel_read_blk(fs->io,
173                                                              file->physblock, 
174                                                              1, file->buf);
175                                 if (retval)
176                                         return retval;
177                         } else
178                                 memset(file->buf, 0, fs->blocksize);
179                 }
180                 file->flags |= EXT2_FILE_BUF_VALID;
181         }
182         return 0;
183 }
184         
185
186 errcode_t ext2fs_file_close(ext2_file_t file)
187 {
188         errcode_t       retval;
189         
190         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
191
192         retval = ext2fs_file_flush(file);
193         
194         if (file->buf)
195                 ext2fs_free_mem((void **) &file->buf);
196         ext2fs_free_mem((void **) &file);
197
198         return retval;
199 }
200
201
202 errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
203                            unsigned int wanted, unsigned int *got)
204 {
205         ext2_filsys     fs;
206         errcode_t       retval = 0;
207         unsigned int    start, left, c, count = 0;
208         char            *ptr = (char *) buf;
209
210         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
211         fs = file->fs;
212
213         while ((file->pos < file->inode.i_size) && (wanted > 0)) {
214                 retval = sync_buffer_position(file);
215                 if (retval)
216                         goto fail;
217                 retval = load_buffer(file, 0);
218                 if (retval)
219                         goto fail;
220
221                 start = file->pos % fs->blocksize;
222                 c = fs->blocksize - start;
223                 if (c > wanted)
224                         c = wanted;
225                 left = file->inode.i_size - file->pos ;
226                 if (c > left)
227                         c = left;
228         
229                 memcpy(ptr, file->buf+start, c);
230                 file->pos += c;
231                 ptr += c;
232                 count += c;
233                 wanted -= c;
234         }
235         
236 fail:
237         if (got)
238                 *got = count;
239         return retval;
240 }
241
242
243 errcode_t ext2fs_file_write(ext2_file_t file, void *buf,
244                             unsigned int nbytes, unsigned int *written)
245 {
246         ext2_filsys     fs;
247         blk_t pb;
248         errcode_t       retval = 0;
249         unsigned int    start, c, count = 0;
250         char            *ptr = (char *) buf;
251         int             dontfill;
252
253         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
254         fs = file->fs;
255
256         if (!(file->flags & EXT2_FILE_WRITE))
257                 return EXT2_ET_FILE_RO;
258
259         while (nbytes > 0) {
260                 retval = sync_buffer_position(file);
261                 if (retval)
262                         goto fail;
263                 
264                 start = file->pos % fs->blocksize;
265                 c = fs->blocksize - start;
266                 if (c > nbytes)
267                         c = nbytes;
268
269                 /*
270                  * We only need to do a read-modify-update cycle if
271                  * we're doing a partial write.
272                  */
273                 retval = load_buffer(file, (c == fs->blocksize));
274                 if (retval)
275                         goto fail;
276
277                 file->flags |= EXT2_FILE_BUF_DIRTY;
278                 memcpy(file->buf+start, ptr, c);
279                 file->pos += c;
280                 ptr += c;
281                 count += c;
282                 nbytes -= c;
283         }
284         
285 fail:
286         if (written)
287                 *written = count;
288         return retval;
289 }
290
291 errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
292                             int whence, ext2_off_t *ret_pos)
293 {
294         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
295
296         if (whence == EXT2_SEEK_SET)
297                 file->pos = offset;
298         else if (whence == EXT2_SEEK_CUR)
299                 file->pos += offset;
300         else if (whence == EXT2_SEEK_END)
301                 file->pos = file->inode.i_size + offset;
302         else
303                 return EXT2_ET_INVALID_ARGUMENT;
304
305         if (ret_pos)
306                 *ret_pos = file->pos;
307
308         return 0;
309 }
310
311 /*
312  * This function returns the size of the file, according to the inode
313  */
314 ext2_off_t ext2fs_file_get_size(ext2_file_t file)
315 {
316         if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
317                 return 0;
318         return file->inode.i_size;
319 }
320
321 /*
322  * This function sets the size of the file, truncating it if necessary
323  * 
324  * XXX still need to call truncate
325  */
326 errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
327 {
328         errcode_t       retval;
329         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
330         
331         file->inode.i_size = size;
332         retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
333         if (retval)
334                 return retval;
335
336         /* 
337          * XXX truncate inode if necessary
338          */
339
340         return 0;
341 }