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