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