Whamcloud - gitweb
misc: clean up compiler warnings
[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 Library
8  * General Public License, version 2.
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         blk64_t                 blockno;
29         blk64_t                 physblock;
30         char                    *buf;
31 };
32
33 #define BMAP_BUFFER (file->buf + fs->blocksize)
34
35 errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
36                             struct ext2_inode *inode,
37                             int flags, ext2_file_t *ret)
38 {
39         ext2_file_t     file;
40         errcode_t       retval;
41
42         /*
43          * Don't let caller create or open a file for writing if the
44          * filesystem is read-only.
45          */
46         if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
47             !(fs->flags & EXT2_FLAG_RW))
48                 return EXT2_ET_RO_FILSYS;
49
50         retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
51         if (retval)
52                 return retval;
53
54         memset(file, 0, sizeof(struct ext2_file));
55         file->magic = EXT2_ET_MAGIC_EXT2_FILE;
56         file->fs = fs;
57         file->ino = ino;
58         file->flags = flags & EXT2_FILE_MASK;
59
60         if (inode) {
61                 memcpy(&file->inode, inode, sizeof(struct ext2_inode));
62         } else {
63                 retval = ext2fs_read_inode(fs, ino, &file->inode);
64                 if (retval)
65                         goto fail;
66         }
67
68         retval = ext2fs_get_array(3, fs->blocksize, &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(&file->buf);
78         ext2fs_free_mem(&file);
79         return retval;
80 }
81
82 errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
83                            int flags, ext2_file_t *ret)
84 {
85         return ext2fs_file_open2(fs, ino, NULL, flags, ret);
86 }
87
88 /*
89  * This function returns the filesystem handle of a file from the structure
90  */
91 ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
92 {
93         if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
94                 return 0;
95         return file->fs;
96 }
97
98 /*
99  * This function returns the pointer to the inode of a file from the structure
100  */
101 struct ext2_inode *ext2fs_file_get_inode(ext2_file_t file)
102 {
103         if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
104                 return NULL;
105         return &file->inode;
106 }
107
108 /*
109  * This function flushes the dirty block buffer out to disk if
110  * necessary.
111  */
112 errcode_t ext2fs_file_flush(ext2_file_t file)
113 {
114         errcode_t       retval;
115         ext2_filsys fs;
116
117         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
118         fs = file->fs;
119
120         if (!(file->flags & EXT2_FILE_BUF_VALID) ||
121             !(file->flags & EXT2_FILE_BUF_DIRTY))
122                 return 0;
123
124         /*
125          * OK, the physical block hasn't been allocated yet.
126          * Allocate it.
127          */
128         if (!file->physblock) {
129                 retval = ext2fs_bmap2(fs, file->ino, &file->inode,
130                                      BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
131                                      file->blockno, 0, &file->physblock);
132                 if (retval)
133                         return retval;
134         }
135
136         retval = io_channel_write_blk(fs->io, file->physblock,
137                                       1, file->buf);
138         if (retval)
139                 return retval;
140
141         file->flags &= ~EXT2_FILE_BUF_DIRTY;
142
143         return retval;
144 }
145
146 /*
147  * This function synchronizes the file's block buffer and the current
148  * file position, possibly invalidating block buffer if necessary
149  */
150 static errcode_t sync_buffer_position(ext2_file_t file)
151 {
152         blk_t   b;
153         errcode_t       retval;
154
155         b = file->pos / file->fs->blocksize;
156         if (b != file->blockno) {
157                 retval = ext2fs_file_flush(file);
158                 if (retval)
159                         return retval;
160                 file->flags &= ~EXT2_FILE_BUF_VALID;
161         }
162         file->blockno = b;
163         return 0;
164 }
165
166 /*
167  * This function loads the file's block buffer with valid data from
168  * the disk as necessary.
169  *
170  * If dontfill is true, then skip initializing the buffer since we're
171  * going to be replacing its entire contents anyway.  If set, then the
172  * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
173  */
174 #define DONTFILL 1
175 static errcode_t load_buffer(ext2_file_t file, int dontfill)
176 {
177         ext2_filsys     fs = file->fs;
178         errcode_t       retval;
179
180         if (!(file->flags & EXT2_FILE_BUF_VALID)) {
181                 retval = ext2fs_bmap2(fs, file->ino, &file->inode,
182                                      BMAP_BUFFER, 0, file->blockno, 0,
183                                      &file->physblock);
184                 if (retval)
185                         return retval;
186                 if (!dontfill) {
187                         if (file->physblock) {
188                                 retval = io_channel_read_blk(fs->io,
189                                                              file->physblock,
190                                                              1, file->buf);
191                                 if (retval)
192                                         return retval;
193                         } else
194                                 memset(file->buf, 0, fs->blocksize);
195                 }
196                 file->flags |= EXT2_FILE_BUF_VALID;
197         }
198         return 0;
199 }
200
201
202 errcode_t ext2fs_file_close(ext2_file_t file)
203 {
204         errcode_t       retval;
205
206         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
207
208         retval = ext2fs_file_flush(file);
209
210         if (file->buf)
211                 ext2fs_free_mem(&file->buf);
212         ext2fs_free_mem(&file);
213
214         return retval;
215 }
216
217
218 errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
219                            unsigned int wanted, unsigned int *got)
220 {
221         ext2_filsys     fs;
222         errcode_t       retval = 0;
223         unsigned int    start, c, count = 0;
224         __u64           left;
225         char            *ptr = (char *) buf;
226
227         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
228         fs = file->fs;
229
230         while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
231                 retval = sync_buffer_position(file);
232                 if (retval)
233                         goto fail;
234                 retval = load_buffer(file, 0);
235                 if (retval)
236                         goto fail;
237
238                 start = file->pos % fs->blocksize;
239                 c = fs->blocksize - start;
240                 if (c > wanted)
241                         c = wanted;
242                 left = EXT2_I_SIZE(&file->inode) - file->pos ;
243                 if (c > left)
244                         c = left;
245
246                 memcpy(ptr, file->buf+start, c);
247                 file->pos += c;
248                 ptr += c;
249                 count += c;
250                 wanted -= c;
251         }
252
253 fail:
254         if (got)
255                 *got = count;
256         return retval;
257 }
258
259
260 errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
261                             unsigned int nbytes, unsigned int *written)
262 {
263         ext2_filsys     fs;
264         errcode_t       retval = 0;
265         unsigned int    start, c, count = 0;
266         const char      *ptr = (const char *) buf;
267
268         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
269         fs = file->fs;
270
271         if (!(file->flags & EXT2_FILE_WRITE))
272                 return EXT2_ET_FILE_RO;
273
274         while (nbytes > 0) {
275                 retval = sync_buffer_position(file);
276                 if (retval)
277                         goto fail;
278
279                 start = file->pos % fs->blocksize;
280                 c = fs->blocksize - start;
281                 if (c > nbytes)
282                         c = nbytes;
283
284                 /*
285                  * We only need to do a read-modify-update cycle if
286                  * we're doing a partial write.
287                  */
288                 retval = load_buffer(file, (c == fs->blocksize));
289                 if (retval)
290                         goto fail;
291
292                 file->flags |= EXT2_FILE_BUF_DIRTY;
293                 memcpy(file->buf+start, ptr, c);
294                 file->pos += c;
295                 ptr += c;
296                 count += c;
297                 nbytes -= c;
298         }
299
300 fail:
301         if (written)
302                 *written = count;
303         return retval;
304 }
305
306 errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
307                             int whence, __u64 *ret_pos)
308 {
309         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
310
311         if (whence == EXT2_SEEK_SET)
312                 file->pos = offset;
313         else if (whence == EXT2_SEEK_CUR)
314                 file->pos += offset;
315         else if (whence == EXT2_SEEK_END)
316                 file->pos = EXT2_I_SIZE(&file->inode) + offset;
317         else
318                 return EXT2_ET_INVALID_ARGUMENT;
319
320         if (ret_pos)
321                 *ret_pos = file->pos;
322
323         return 0;
324 }
325
326 errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
327                             int whence, ext2_off_t *ret_pos)
328 {
329         __u64           loffset, ret_loffset;
330         errcode_t       retval;
331
332         loffset = offset;
333         retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
334         if (ret_pos)
335                 *ret_pos = (ext2_off_t) ret_loffset;
336         return retval;
337 }
338
339
340 /*
341  * This function returns the size of the file, according to the inode
342  */
343 errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
344 {
345         if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
346                 return EXT2_ET_MAGIC_EXT2_FILE;
347         *ret_size = EXT2_I_SIZE(&file->inode);
348         return 0;
349 }
350
351 /*
352  * This function returns the size of the file, according to the inode
353  */
354 ext2_off_t ext2fs_file_get_size(ext2_file_t file)
355 {
356         __u64   size;
357
358         if (ext2fs_file_get_lsize(file, &size))
359                 return 0;
360         if ((size >> 32) != 0)
361                 return 0;
362         return size;
363 }
364
365 /*
366  * This function sets the size of the file, truncating it if necessary
367  *
368  */
369 errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size)
370 {
371         ext2_off64_t    old_size;
372         errcode_t       retval;
373         blk64_t         old_truncate, truncate_block;
374
375         EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
376
377         truncate_block = ((size + file->fs->blocksize - 1) >>
378                           EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1;
379         old_size = file->inode.i_size +
380                 ((blk64_t)file->inode.i_size_high << 32);
381         old_truncate = ((old_size + file->fs->blocksize - 1) >>
382                       EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1;
383
384         file->inode.i_size = size & 0xffffffff;
385         file->inode.i_size_high = (size >> 32);
386         if (file->ino) {
387                 retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
388                 if (retval)
389                         return retval;
390         }
391
392         if (truncate_block <= old_truncate)
393                 return 0;
394
395         return ext2fs_punch(file->fs, file->ino, &file->inode, 0,
396                             truncate_block, ~0ULL);
397 }
398
399 errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
400 {
401         return ext2fs_file_set_size2(file, size);
402 }