Whamcloud - gitweb
Add new inode I/O abstraction interface which exports an inode as
[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 #include "ext2_fs.h"
19 #include "ext2fs.h"
20
21 struct ext2_file {
22         errcode_t               magic;
23         ext2_filsys             fs;
24         ext2_ino_t              ino;
25         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
33 #define BMAP_BUFFER (file->buf + fs->blocksize)
34
35 errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
36                            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
49         retval = ext2fs_get_mem(sizeof(struct ext2_file), (void **) &file);
50         if (retval)
51                 return retval;
52         
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         
63         retval = ext2fs_get_mem(fs->blocksize * 3, (void **) &file->buf);
64         if (retval)
65                 goto fail;
66
67         *ret = file;
68         return 0;
69         
70 fail:
71         if (file->buf)
72                 ext2fs_free_mem((void **) &file->buf);
73         ext2fs_free_mem((void **) &file);
74         return retval;
75 }
76
77 /*
78  * This function returns the filesystem handle of a file from the structure
79  */
80 ext2_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 /*
88  * This function flushes the dirty block buffer out to disk if
89  * necessary.
90  */
91 errcode_t ext2fs_file_flush(ext2_file_t file)
92 {
93         errcode_t       retval;
94         ext2_filsys fs;
95         
96         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
97         fs = file->fs;
98
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) {
108                 retval = ext2fs_bmap(fs, file->ino, &file->inode,
109                                      BMAP_BUFFER, BMAP_ALLOC,
110                                      file->blockno, &file->physblock);
111                 if (retval)
112                         return retval;
113         }
114
115         retval = io_channel_write_blk(fs->io, file->physblock,
116                                       1, file->buf);
117         if (retval)
118                 return retval;
119
120         file->flags &= ~EXT2_FILE_BUF_DIRTY;
121
122         return retval;
123 }
124
125 /*
126  * This function synchronizes the file's block buffer and the current
127  * file position, possibly invalidating block buffer if necessary
128  */
129 static 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
154 static 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
181 errcode_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)
190                 ext2fs_free_mem((void **) &file->buf);
191         ext2fs_free_mem((void **) &file);
192
193         return retval;
194 }
195
196
197 errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
198                            unsigned int wanted, unsigned int *got)
199 {
200         ext2_filsys     fs;
201         errcode_t       retval = 0;
202         unsigned int    start, left, c, count = 0;
203         char            *ptr = (char *) buf;
204
205         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
206         fs = file->fs;
207
208         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;
215
216                 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;
223         
224                 memcpy(ptr, file->buf+start, c);
225                 file->pos += c;
226                 ptr += c;
227                 count += c;
228                 wanted -= c;
229         }
230         
231 fail:
232         if (got)
233                 *got = count;
234         return retval;
235 }
236
237
238 errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
239                             unsigned int nbytes, unsigned int *written)
240 {
241         ext2_filsys     fs;
242         errcode_t       retval = 0;
243         unsigned int    start, c, count = 0;
244         char            *ptr = (char *) buf;
245
246         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
247         fs = file->fs;
248
249         if (!(file->flags & EXT2_FILE_WRITE))
250                 return EXT2_ET_FILE_RO;
251
252         while (nbytes > 0) {
253                 retval = sync_buffer_position(file);
254                 if (retval)
255                         goto fail;
256                 
257                 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));
267                 if (retval)
268                         goto fail;
269
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;
276         }
277         
278 fail:
279         if (written)
280                 *written = count;
281         return retval;
282 }
283
284 errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
285                             int whence, ext2_off_t *ret_pos)
286 {
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
296                 return EXT2_ET_INVALID_ARGUMENT;
297
298         if (ret_pos)
299                 *ret_pos = file->pos;
300
301         return 0;
302 }
303
304 /*
305  * This function returns the size of the file, according to the inode
306  */
307 ext2_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 }
313
314 /*
315  * This function sets the size of the file, truncating it if necessary
316  * 
317  * XXX still need to call truncate
318  */
319 errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
320 {
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 }