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