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