Whamcloud - gitweb
libext2fs: make symlinks safe for 64bit blocks and extents
[tools/e2fsprogs.git] / lib / ext2fs / inode_io.c
1 /*
2  * inode_io.c --- This is allows an inode in an ext2 filesystem image
3  *      to be accessed via the I/O manager interface.
4  *
5  * Copyright (C) 2002 Theodore Ts'o.
6  *
7  * %Begin-Header%
8  * This file may be redistributed under the terms of the GNU Library
9  * General Public License, version 2.
10  * %End-Header%
11  */
12
13 #include "config.h"
14 #include <stdio.h>
15 #include <string.h>
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #if HAVE_ERRNO_H
20 #include <errno.h>
21 #endif
22 #include <time.h>
23
24 #include "ext2_fs.h"
25 #include "ext2fs.h"
26
27 /*
28  * For checking structure magic numbers...
29  */
30
31 #define EXT2_CHECK_MAGIC(struct, code) \
32           if ((struct)->magic != (code)) return (code)
33
34 struct inode_private_data {
35         int                             magic;
36         char                            name[32];
37         ext2_file_t                     file;
38         ext2_filsys                     fs;
39         ext2_ino_t                      ino;
40         struct ext2_inode               inode;
41         int                             flags;
42         struct inode_private_data       *next;
43 };
44
45 #define CHANNEL_HAS_INODE       0x8000
46
47 static struct inode_private_data *top_intern;
48 static int ino_unique = 0;
49
50 static errcode_t inode_open(const char *name, int flags, io_channel *channel);
51 static errcode_t inode_close(io_channel channel);
52 static errcode_t inode_set_blksize(io_channel channel, int blksize);
53 static errcode_t inode_read_blk(io_channel channel, unsigned long block,
54                                int count, void *data);
55 static errcode_t inode_write_blk(io_channel channel, unsigned long block,
56                                 int count, const void *data);
57 static errcode_t inode_flush(io_channel channel);
58 static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
59                                 int size, const void *data);
60 static errcode_t inode_read_blk64(io_channel channel,
61                                 unsigned long long block, int count, void *data);
62 static errcode_t inode_write_blk64(io_channel channel,
63                                 unsigned long long block, int count, const void *data);
64
65 static struct struct_io_manager struct_inode_manager = {
66         EXT2_ET_MAGIC_IO_MANAGER,
67         "Inode I/O Manager",
68         inode_open,
69         inode_close,
70         inode_set_blksize,
71         inode_read_blk,
72         inode_write_blk,
73         inode_flush,
74         inode_write_byte,
75         NULL,
76         NULL,
77         inode_read_blk64,
78         inode_write_blk64
79 };
80
81 io_manager inode_io_manager = &struct_inode_manager;
82
83 errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
84                                   struct ext2_inode *inode,
85                                   char **name)
86 {
87         struct inode_private_data       *data;
88         errcode_t                       retval;
89
90         if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data),
91                                      &data)))
92                 return retval;
93         data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL;
94         sprintf(data->name, "%u:%d", ino, ino_unique++);
95         data->file = 0;
96         data->fs = fs;
97         data->ino = ino;
98         data->flags = 0;
99         if (inode) {
100                 memcpy(&data->inode, inode, sizeof(struct ext2_inode));
101                 data->flags |= CHANNEL_HAS_INODE;
102         }
103         data->next = top_intern;
104         top_intern = data;
105         *name = data->name;
106         return 0;
107 }
108
109 errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
110                                  char **name)
111 {
112         return ext2fs_inode_io_intern2(fs, ino, NULL, name);
113 }
114
115
116 static errcode_t inode_open(const char *name, int flags, io_channel *channel)
117 {
118         io_channel      io = NULL;
119         struct inode_private_data *prev, *data = NULL;
120         errcode_t       retval;
121         int             open_flags;
122
123         if (name == 0)
124                 return EXT2_ET_BAD_DEVICE_NAME;
125
126         for (data = top_intern, prev = NULL; data;
127              prev = data, data = data->next)
128                 if (strcmp(name, data->name) == 0)
129                         break;
130         if (!data)
131                 return ENOENT;
132         if (prev)
133                 prev->next = data->next;
134         else
135                 top_intern = data->next;
136
137         retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
138         if (retval)
139                 goto cleanup;
140         memset(io, 0, sizeof(struct struct_io_channel));
141
142         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
143         io->manager = inode_io_manager;
144         retval = ext2fs_get_mem(strlen(name)+1, &io->name);
145         if (retval)
146                 goto cleanup;
147
148         strcpy(io->name, name);
149         io->private_data = data;
150         io->block_size = 1024;
151         io->read_error = 0;
152         io->write_error = 0;
153         io->refcount = 1;
154
155         open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0;
156         retval = ext2fs_file_open2(data->fs, data->ino,
157                                    (data->flags & CHANNEL_HAS_INODE) ?
158                                    &data->inode : 0, open_flags,
159                                    &data->file);
160         if (retval)
161                 goto cleanup;
162
163         *channel = io;
164         return 0;
165
166 cleanup:
167         if (io && io->name)
168                 ext2fs_free_mem(&io->name);
169         if (data)
170                 ext2fs_free_mem(&data);
171         if (io)
172                 ext2fs_free_mem(&io);
173         return retval;
174 }
175
176 static errcode_t inode_close(io_channel channel)
177 {
178         struct inode_private_data *data;
179         errcode_t       retval = 0;
180
181         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
182         data = (struct inode_private_data *) channel->private_data;
183         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
184
185         if (--channel->refcount > 0)
186                 return 0;
187
188         retval = ext2fs_file_close(data->file);
189
190         ext2fs_free_mem(&channel->private_data);
191         if (channel->name)
192                 ext2fs_free_mem(&channel->name);
193         ext2fs_free_mem(&channel);
194         return retval;
195 }
196
197 static errcode_t inode_set_blksize(io_channel channel, int blksize)
198 {
199         struct inode_private_data *data;
200
201         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
202         data = (struct inode_private_data *) channel->private_data;
203         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
204
205         channel->block_size = blksize;
206         return 0;
207 }
208
209
210 static errcode_t inode_read_blk64(io_channel channel,
211                                 unsigned long long block, int count, void *buf)
212 {
213         struct inode_private_data *data;
214         errcode_t       retval;
215
216         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
217         data = (struct inode_private_data *) channel->private_data;
218         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
219
220         if ((retval = ext2fs_file_lseek(data->file,
221                                         block * channel->block_size,
222                                         EXT2_SEEK_SET, 0)))
223                 return retval;
224
225         count = (count < 0) ? -count : (count * channel->block_size);
226
227         return ext2fs_file_read(data->file, buf, count, 0);
228 }
229
230 static errcode_t inode_read_blk(io_channel channel, unsigned long block,
231                                int count, void *buf)
232 {
233         return inode_read_blk64(channel, block, count, buf);
234 }
235
236 static errcode_t inode_write_blk64(io_channel channel,
237                                 unsigned long long block, int count, const void *buf)
238 {
239         struct inode_private_data *data;
240         errcode_t       retval;
241
242         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
243         data = (struct inode_private_data *) channel->private_data;
244         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
245
246         if ((retval = ext2fs_file_lseek(data->file,
247                                         block * channel->block_size,
248                                         EXT2_SEEK_SET, 0)))
249                 return retval;
250
251         count = (count < 0) ? -count : (count * channel->block_size);
252
253         return ext2fs_file_write(data->file, buf, count, 0);
254 }
255
256 static errcode_t inode_write_blk(io_channel channel, unsigned long block,
257                                 int count, const void *buf)
258 {
259         return inode_write_blk64(channel, block, count, buf);
260 }
261
262 static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
263                                  int size, const void *buf)
264 {
265         struct inode_private_data *data;
266         errcode_t       retval = 0;
267
268         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
269         data = (struct inode_private_data *) channel->private_data;
270         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
271
272         if ((retval = ext2fs_file_lseek(data->file, offset,
273                                         EXT2_SEEK_SET, 0)))
274                 return retval;
275
276         return ext2fs_file_write(data->file, buf, size, 0);
277 }
278
279 /*
280  * Flush data buffers to disk.
281  */
282 static errcode_t inode_flush(io_channel channel)
283 {
284         struct inode_private_data *data;
285
286         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
287         data = (struct inode_private_data *) channel->private_data;
288         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
289
290         return ext2fs_file_flush(data->file);
291 }
292