Whamcloud - gitweb
ext2fs_getmem(), ext2fs_free_mem(), and ext2fs_resize_mem()
[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         __u64                   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), &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, &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(&file->buf);
73         ext2fs_free_mem(&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(&file->buf);
191         ext2fs_free_mem(&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, c, count = 0;
203         __u64           left;
204         char            *ptr = (char *) buf;
205
206         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
207         fs = file->fs;
208
209         while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
210                 retval = sync_buffer_position(file);
211                 if (retval)
212                         goto fail;
213                 retval = load_buffer(file, 0);
214                 if (retval)
215                         goto fail;
216
217                 start = file->pos % fs->blocksize;
218                 c = fs->blocksize - start;
219                 if (c > wanted)
220                         c = wanted;
221                 left = EXT2_I_SIZE(&file->inode) - file->pos ;
222                 if (c > left)
223                         c = left;
224         
225                 memcpy(ptr, file->buf+start, c);
226                 file->pos += c;
227                 ptr += c;
228                 count += c;
229                 wanted -= c;
230         }
231         
232 fail:
233         if (got)
234                 *got = count;
235         return retval;
236 }
237
238
239 errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
240                             unsigned int nbytes, unsigned int *written)
241 {
242         ext2_filsys     fs;
243         errcode_t       retval = 0;
244         unsigned int    start, c, count = 0;
245         const char      *ptr = (const char *) buf;
246
247         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
248         fs = file->fs;
249
250         if (!(file->flags & EXT2_FILE_WRITE))
251                 return EXT2_ET_FILE_RO;
252
253         while (nbytes > 0) {
254                 retval = sync_buffer_position(file);
255                 if (retval)
256                         goto fail;
257                 
258                 start = file->pos % fs->blocksize;
259                 c = fs->blocksize - start;
260                 if (c > nbytes)
261                         c = nbytes;
262
263                 /*
264                  * We only need to do a read-modify-update cycle if
265                  * we're doing a partial write.
266                  */
267                 retval = load_buffer(file, (c == fs->blocksize));
268                 if (retval)
269                         goto fail;
270
271                 file->flags |= EXT2_FILE_BUF_DIRTY;
272                 memcpy(file->buf+start, ptr, c);
273                 file->pos += c;
274                 ptr += c;
275                 count += c;
276                 nbytes -= c;
277         }
278         
279 fail:
280         if (written)
281                 *written = count;
282         return retval;
283 }
284
285 errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
286                             int whence, __u64 *ret_pos)
287 {
288         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
289
290         if (whence == EXT2_SEEK_SET)
291                 file->pos = offset;
292         else if (whence == EXT2_SEEK_CUR)
293                 file->pos += offset;
294         else if (whence == EXT2_SEEK_END)
295                 file->pos = EXT2_I_SIZE(&file->inode) + offset;
296         else
297                 return EXT2_ET_INVALID_ARGUMENT;
298
299         if (ret_pos)
300                 *ret_pos = file->pos;
301
302         return 0;
303 }
304
305 errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
306                             int whence, ext2_off_t *ret_pos)
307 {
308         __u64           loffset, ret_loffset;
309         errcode_t       retval;
310         
311         loffset = offset;
312         retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
313         if (ret_pos)
314                 *ret_pos = (ext2_off_t) ret_loffset;
315         return retval;
316 }
317
318
319 /*
320  * This function returns the size of the file, according to the inode
321  */
322 errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
323 {
324         if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
325                 return EXT2_ET_MAGIC_EXT2_FILE;
326         *ret_size = EXT2_I_SIZE(&file->inode);
327         return 0;
328 }
329
330 /*
331  * This function returns the size of the file, according to the inode
332  */
333 ext2_off_t ext2fs_file_get_size(ext2_file_t file)
334 {
335         __u64   size;
336
337         if (ext2fs_file_get_lsize(file, &size))
338                 return 0;
339         if ((size >> 32) != 0)
340                 return 0;
341         return size;
342 }
343
344 /*
345  * This function sets the size of the file, truncating it if necessary
346  * 
347  * XXX still need to call truncate
348  */
349 errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
350 {
351         errcode_t       retval;
352         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
353         
354         file->inode.i_size = size;
355         file->inode.i_size_high = 0;
356         retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
357         if (retval)
358                 return retval;
359
360         /* 
361          * XXX truncate inode if necessary
362          */
363
364         return 0;
365 }