]>
Commit | Line | Data |
---|---|---|
30fab293 TT |
1 | /* |
2 | * fileio.c --- Simple file I/O routines | |
efc6f628 | 3 | * |
30fab293 TT |
4 | * Copyright (C) 1997 Theodore Ts'o. |
5 | * | |
6 | * %Begin-Header% | |
543547a5 TT |
7 | * This file may be redistributed under the terms of the GNU Library |
8 | * General Public License, version 2. | |
30fab293 TT |
9 | * %End-Header% |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <string.h> | |
14 | #if HAVE_UNISTD_H | |
15 | #include <unistd.h> | |
16 | #endif | |
30fab293 | 17 | |
b5abe6fa | 18 | #include "ext2_fs.h" |
30fab293 TT |
19 | #include "ext2fs.h" |
20 | ||
21 | struct ext2_file { | |
22 | errcode_t magic; | |
23 | ext2_filsys fs; | |
31dbecd4 | 24 | ext2_ino_t ino; |
30fab293 TT |
25 | struct ext2_inode inode; |
26 | int flags; | |
819157db | 27 | __u64 pos; |
319158f9 JS |
28 | blk64_t blockno; |
29 | blk64_t physblock; | |
30fab293 TT |
30 | char *buf; |
31 | }; | |
32 | ||
0f679459 TT |
33 | #define BMAP_BUFFER (file->buf + fs->blocksize) |
34 | ||
a435ec34 TT |
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) | |
30fab293 TT |
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 | ||
c4e3d3f3 | 50 | retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); |
7b4e4534 TT |
51 | if (retval) |
52 | return retval; | |
efc6f628 | 53 | |
30fab293 TT |
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 | ||
a435ec34 TT |
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 | } | |
efc6f628 | 67 | |
ee01079a | 68 | retval = ext2fs_get_array(3, fs->blocksize, &file->buf); |
7b4e4534 | 69 | if (retval) |
30fab293 | 70 | goto fail; |
30fab293 TT |
71 | |
72 | *ret = file; | |
73 | return 0; | |
efc6f628 | 74 | |
30fab293 TT |
75 | fail: |
76 | if (file->buf) | |
c4e3d3f3 TT |
77 | ext2fs_free_mem(&file->buf); |
78 | ext2fs_free_mem(&file); | |
30fab293 TT |
79 | return retval; |
80 | } | |
81 | ||
a435ec34 TT |
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 | ||
79a90bda TT |
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 | ||
6dc058bd RD |
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 | ||
30fab293 TT |
108 | /* |
109 | * This function flushes the dirty block buffer out to disk if | |
110 | * necessary. | |
111 | */ | |
f12e285f | 112 | errcode_t ext2fs_file_flush(ext2_file_t file) |
30fab293 TT |
113 | { |
114 | errcode_t retval; | |
0f679459 | 115 | ext2_filsys fs; |
efc6f628 | 116 | |
30fab293 | 117 | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
0f679459 | 118 | fs = file->fs; |
30fab293 TT |
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) { | |
319158f9 | 129 | retval = ext2fs_bmap2(fs, file->ino, &file->inode, |
a435ec34 | 130 | BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, |
319158f9 | 131 | file->blockno, 0, &file->physblock); |
30fab293 TT |
132 | if (retval) |
133 | return retval; | |
134 | } | |
135 | ||
0f679459 | 136 | retval = io_channel_write_blk(fs->io, file->physblock, |
30fab293 TT |
137 | 1, file->buf); |
138 | if (retval) | |
139 | return retval; | |
140 | ||
141 | file->flags &= ~EXT2_FILE_BUF_DIRTY; | |
142 | ||
143 | return retval; | |
144 | } | |
145 | ||
0f679459 TT |
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)) { | |
319158f9 JS |
181 | retval = ext2fs_bmap2(fs, file->ino, &file->inode, |
182 | BMAP_BUFFER, 0, file->blockno, 0, | |
0f679459 TT |
183 | &file->physblock); |
184 | if (retval) | |
185 | return retval; | |
186 | if (!dontfill) { | |
187 | if (file->physblock) { | |
188 | retval = io_channel_read_blk(fs->io, | |
efc6f628 | 189 | file->physblock, |
0f679459 TT |
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 | } | |
efc6f628 | 200 | |
0f679459 | 201 | |
30fab293 TT |
202 | errcode_t ext2fs_file_close(ext2_file_t file) |
203 | { | |
204 | errcode_t retval; | |
efc6f628 | 205 | |
30fab293 TT |
206 | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
207 | ||
208 | retval = ext2fs_file_flush(file); | |
efc6f628 | 209 | |
30fab293 | 210 | if (file->buf) |
c4e3d3f3 TT |
211 | ext2fs_free_mem(&file->buf); |
212 | ext2fs_free_mem(&file); | |
30fab293 TT |
213 | |
214 | return retval; | |
215 | } | |
216 | ||
217 | ||
218 | errcode_t ext2fs_file_read(ext2_file_t file, void *buf, | |
79a90bda | 219 | unsigned int wanted, unsigned int *got) |
30fab293 TT |
220 | { |
221 | ext2_filsys fs; | |
0f679459 | 222 | errcode_t retval = 0; |
819157db TT |
223 | unsigned int start, c, count = 0; |
224 | __u64 left; | |
b5abe6fa | 225 | char *ptr = (char *) buf; |
30fab293 TT |
226 | |
227 | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); | |
228 | fs = file->fs; | |
229 | ||
819157db | 230 | while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { |
0f679459 | 231 | retval = sync_buffer_position(file); |
30fab293 TT |
232 | if (retval) |
233 | goto fail; | |
0f679459 | 234 | retval = load_buffer(file, 0); |
30fab293 TT |
235 | if (retval) |
236 | goto fail; | |
0f679459 TT |
237 | |
238 | start = file->pos % fs->blocksize; | |
239 | c = fs->blocksize - start; | |
240 | if (c > wanted) | |
241 | c = wanted; | |
819157db | 242 | left = EXT2_I_SIZE(&file->inode) - file->pos ; |
0f679459 TT |
243 | if (c > left) |
244 | c = left; | |
efc6f628 | 245 | |
0f679459 TT |
246 | memcpy(ptr, file->buf+start, c); |
247 | file->pos += c; | |
248 | ptr += c; | |
249 | count += c; | |
250 | wanted -= c; | |
30fab293 | 251 | } |
efc6f628 | 252 | |
0f679459 | 253 | fail: |
30fab293 TT |
254 | if (got) |
255 | *got = count; | |
30fab293 TT |
256 | return retval; |
257 | } | |
258 | ||
259 | ||
f12e285f | 260 | errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, |
79a90bda | 261 | unsigned int nbytes, unsigned int *written) |
30fab293 TT |
262 | { |
263 | ext2_filsys fs; | |
0f679459 TT |
264 | errcode_t retval = 0; |
265 | unsigned int start, c, count = 0; | |
546a1ff1 | 266 | const char *ptr = (const char *) buf; |
30fab293 TT |
267 | |
268 | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); | |
269 | fs = file->fs; | |
270 | ||
271 | if (!(file->flags & EXT2_FILE_WRITE)) | |
1f0b6c1f | 272 | return EXT2_ET_FILE_RO; |
30fab293 | 273 | |
0f679459 TT |
274 | while (nbytes > 0) { |
275 | retval = sync_buffer_position(file); | |
30fab293 TT |
276 | if (retval) |
277 | goto fail; | |
efc6f628 | 278 | |
0f679459 TT |
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)); | |
30fab293 TT |
289 | if (retval) |
290 | goto fail; | |
0f679459 TT |
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; | |
30fab293 | 298 | } |
efc6f628 | 299 | |
0f679459 | 300 | fail: |
30fab293 TT |
301 | if (written) |
302 | *written = count; | |
30fab293 TT |
303 | return retval; |
304 | } | |
305 | ||
819157db TT |
306 | errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, |
307 | int whence, __u64 *ret_pos) | |
30fab293 TT |
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) | |
819157db | 316 | file->pos = EXT2_I_SIZE(&file->inode) + offset; |
30fab293 | 317 | else |
1f0b6c1f | 318 | return EXT2_ET_INVALID_ARGUMENT; |
30fab293 TT |
319 | |
320 | if (ret_pos) | |
321 | *ret_pos = file->pos; | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
819157db TT |
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; | |
efc6f628 | 331 | |
819157db TT |
332 | loffset = offset; |
333 | retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); | |
20754488 TT |
334 | if (ret_pos) |
335 | *ret_pos = (ext2_off_t) ret_loffset; | |
819157db TT |
336 | return retval; |
337 | } | |
338 | ||
339 | ||
79a90bda TT |
340 | /* |
341 | * This function returns the size of the file, according to the inode | |
342 | */ | |
819157db | 343 | errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) |
79a90bda TT |
344 | { |
345 | if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) | |
819157db TT |
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) | |
79a90bda | 361 | return 0; |
819157db | 362 | return size; |
79a90bda | 363 | } |
30fab293 | 364 | |
79a90bda TT |
365 | /* |
366 | * This function sets the size of the file, truncating it if necessary | |
efc6f628 | 367 | * |
79a90bda | 368 | */ |
27a0e958 | 369 | errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size) |
79a90bda | 370 | { |
27a0e958 | 371 | ext2_off64_t old_size; |
79a90bda | 372 | errcode_t retval; |
27a0e958 TT |
373 | blk64_t old_truncate, truncate_block; |
374 | ||
79a90bda | 375 | EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); |
efc6f628 | 376 | |
27a0e958 TT |
377 | truncate_block = ((size + file->fs->blocksize - 1) >> |
378 | EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; | |
0bd0e593 | 379 | old_size = EXT2_I_SIZE(&file->inode); |
27a0e958 TT |
380 | old_truncate = ((old_size + file->fs->blocksize - 1) >> |
381 | EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; | |
382 | ||
383 | file->inode.i_size = size & 0xffffffff; | |
384 | file->inode.i_size_high = (size >> 32); | |
a435ec34 TT |
385 | if (file->ino) { |
386 | retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); | |
387 | if (retval) | |
388 | return retval; | |
389 | } | |
79a90bda | 390 | |
27a0e958 TT |
391 | if (truncate_block <= old_truncate) |
392 | return 0; | |
79a90bda | 393 | |
27a0e958 TT |
394 | return ext2fs_punch(file->fs, file->ino, &file->inode, 0, |
395 | truncate_block, ~0ULL); | |
396 | } | |
397 | ||
398 | errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) | |
399 | { | |
400 | return ext2fs_file_set_size2(file, size); | |
79a90bda | 401 | } |