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