• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * undo_io.c --- This is the undo io manager that copies the old data that
3  * copies the old data being overwritten into a tdb database
4  *
5  * Copyright IBM Corporation, 2007
6  * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
7  *
8  * %Begin-Header%
9  * This file may be redistributed under the terms of the GNU Library
10  * General Public License, version 2.
11  * %End-Header%
12  */
13 
14 #define _LARGEFILE_SOURCE
15 #define _LARGEFILE64_SOURCE
16 
17 #include <stdio.h>
18 #include <string.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #if HAVE_ERRNO_H
23 #include <errno.h>
24 #endif
25 #include <fcntl.h>
26 #include <time.h>
27 #ifdef __linux__
28 #include <sys/utsname.h>
29 #endif
30 #if HAVE_SYS_STAT_H
31 #include <sys/stat.h>
32 #endif
33 #if HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36 #if HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
38 #endif
39 
40 #include "tdb.h"
41 
42 #include "ext2_fs.h"
43 #include "ext2fs.h"
44 
45 #ifdef __GNUC__
46 #define ATTR(x) __attribute__(x)
47 #else
48 #define ATTR(x)
49 #endif
50 
51 /*
52  * For checking structure magic numbers...
53  */
54 
55 #define EXT2_CHECK_MAGIC(struct, code) \
56 	  if ((struct)->magic != (code)) return (code)
57 
58 struct undo_private_data {
59 	int	magic;
60 	TDB_CONTEXT *tdb;
61 	char *tdb_file;
62 
63 	/* The backing io channel */
64 	io_channel real;
65 
66 	int tdb_data_size;
67 	int tdb_written;
68 
69 	/* to support offset in unix I/O manager */
70 	ext2_loff_t offset;
71 };
72 
73 static errcode_t undo_open(const char *name, int flags, io_channel *channel);
74 static errcode_t undo_close(io_channel channel);
75 static errcode_t undo_set_blksize(io_channel channel, int blksize);
76 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
77 			       int count, void *data);
78 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
79 				int count, const void *data);
80 static errcode_t undo_flush(io_channel channel);
81 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
82 				int size, const void *data);
83 static errcode_t undo_set_option(io_channel channel, const char *option,
84 				 const char *arg);
85 
86 static struct struct_io_manager struct_undo_manager = {
87 	EXT2_ET_MAGIC_IO_MANAGER,
88 	"Undo I/O Manager",
89 	undo_open,
90 	undo_close,
91 	undo_set_blksize,
92 	undo_read_blk,
93 	undo_write_blk,
94 	undo_flush,
95 	undo_write_byte,
96 	undo_set_option
97 };
98 
99 io_manager undo_io_manager = &struct_undo_manager;
100 static io_manager undo_io_backing_manager ;
101 static char *tdb_file;
102 static int actual_size;
103 
104 static unsigned char mtime_key[] = "filesystem MTIME";
105 static unsigned char blksize_key[] = "filesystem BLKSIZE";
106 static unsigned char uuid_key[] = "filesystem UUID";
107 
set_undo_io_backing_manager(io_manager manager)108 errcode_t set_undo_io_backing_manager(io_manager manager)
109 {
110 	/*
111 	 * We may want to do some validation later
112 	 */
113 	undo_io_backing_manager = manager;
114 	return 0;
115 }
116 
set_undo_io_backup_file(char * file_name)117 errcode_t set_undo_io_backup_file(char *file_name)
118 {
119 	tdb_file = strdup(file_name);
120 
121 	if (tdb_file == NULL) {
122 		return EXT2_ET_NO_MEMORY;
123 	}
124 
125 	return 0;
126 }
127 
write_file_system_identity(io_channel undo_channel,TDB_CONTEXT * tdb)128 static errcode_t write_file_system_identity(io_channel undo_channel,
129 							TDB_CONTEXT *tdb)
130 {
131 	errcode_t retval;
132 	struct ext2_super_block super;
133 	TDB_DATA tdb_key, tdb_data;
134 	struct undo_private_data *data;
135 	io_channel channel;
136 	int block_size ;
137 
138 	data = (struct undo_private_data *) undo_channel->private_data;
139 	channel = data->real;
140 	block_size = channel->block_size;
141 
142 	io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
143 	retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super);
144 	if (retval)
145 		goto err_out;
146 
147 	/* Write to tdb file in the file system byte order */
148 	tdb_key.dptr = mtime_key;
149 	tdb_key.dsize = sizeof(mtime_key);
150 	tdb_data.dptr = (unsigned char *) &(super.s_mtime);
151 	tdb_data.dsize = sizeof(super.s_mtime);
152 
153 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
154 	if (retval == -1) {
155 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
156 		goto err_out;
157 	}
158 
159 	tdb_key.dptr = uuid_key;
160 	tdb_key.dsize = sizeof(uuid_key);
161 	tdb_data.dptr = (unsigned char *)&(super.s_uuid);
162 	tdb_data.dsize = sizeof(super.s_uuid);
163 
164 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
165 	if (retval == -1) {
166 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
167 	}
168 
169 err_out:
170 	io_channel_set_blksize(channel, block_size);
171 	return retval;
172 }
173 
write_block_size(TDB_CONTEXT * tdb,int block_size)174 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
175 {
176 	errcode_t retval;
177 	TDB_DATA tdb_key, tdb_data;
178 
179 	tdb_key.dptr = blksize_key;
180 	tdb_key.dsize = sizeof(blksize_key);
181 	tdb_data.dptr = (unsigned char *)&(block_size);
182 	tdb_data.dsize = sizeof(block_size);
183 
184 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
185 	if (retval == -1) {
186 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
187 	}
188 
189 	return retval;
190 }
191 
undo_write_tdb(io_channel channel,unsigned long block,int count)192 static errcode_t undo_write_tdb(io_channel channel,
193 				unsigned long block, int count)
194 
195 {
196 	int size, sz;
197 	unsigned long block_num, backing_blk_num;
198 	errcode_t retval = 0;
199 	ext2_loff_t offset;
200 	struct undo_private_data *data;
201 	TDB_DATA tdb_key, tdb_data;
202 	unsigned char *read_ptr;
203 	unsigned long end_block;
204 
205 	data = (struct undo_private_data *) channel->private_data;
206 
207 	if (data->tdb == NULL) {
208 		/*
209 		 * Transaction database not initialized
210 		 */
211 		return 0;
212 	}
213 
214 	if (count == 1)
215 		size = channel->block_size;
216 	else {
217 		if (count < 0)
218 			size = -count;
219 		else
220 			size = count * channel->block_size;
221 	}
222 	/*
223 	 * Data is stored in tdb database as blocks of tdb_data_size size
224 	 * This helps in efficient lookup further.
225 	 *
226 	 * We divide the disk to blocks of tdb_data_size.
227 	 */
228 	offset = (block * channel->block_size) + data->offset ;
229 	block_num = offset / data->tdb_data_size;
230 	end_block = (offset + size) / data->tdb_data_size;
231 
232 	tdb_transaction_start(data->tdb);
233 	while (block_num <= end_block ) {
234 
235 		tdb_key.dptr = (unsigned char *)&block_num;
236 		tdb_key.dsize = sizeof(block_num);
237 		/*
238 		 * Check if we have the record already
239 		 */
240 		if (tdb_exists(data->tdb, tdb_key)) {
241 			/* Try the next block */
242 			block_num++;
243 			continue;
244 		}
245 		/*
246 		 * Read one block using the backing I/O manager
247 		 * The backing I/O manager block size may be
248 		 * different from the tdb_data_size.
249 		 * Also we need to recalcuate the block number with respect
250 		 * to the backing I/O manager.
251 		 */
252 		offset = block_num * data->tdb_data_size;
253 		backing_blk_num = (offset - data->offset) / channel->block_size;
254 
255 		count = data->tdb_data_size +
256 				((offset - data->offset) % channel->block_size);
257 		retval = ext2fs_get_mem(count, &read_ptr);
258 		if (retval) {
259 			tdb_transaction_cancel(data->tdb);
260 			return retval;
261 		}
262 
263 		memset(read_ptr, 0, count);
264 		actual_size = 0;
265 		if ((count % channel->block_size) == 0)
266 			sz = count / channel->block_size;
267 		else
268 			sz = -count;
269 		retval = io_channel_read_blk(data->real, backing_blk_num,
270 					     sz, read_ptr);
271 		if (retval) {
272 			if (retval != EXT2_ET_SHORT_READ) {
273 				free(read_ptr);
274 				tdb_transaction_cancel(data->tdb);
275 				return retval;
276 			}
277 			/*
278 			 * short read so update the record size
279 			 * accordingly
280 			 */
281 			tdb_data.dsize = actual_size;
282 		} else {
283 			tdb_data.dsize = data->tdb_data_size;
284 		}
285 		tdb_data.dptr = read_ptr +
286 				((offset - data->offset) % channel->block_size);
287 #ifdef DEBUG
288 		printf("Printing with key %ld data %x and size %d\n",
289 		       block_num,
290 		       tdb_data.dptr,
291 		       tdb_data.dsize);
292 #endif
293 		if (!data->tdb_written) {
294 			data->tdb_written = 1;
295 			/* Write the blocksize to tdb file */
296 			retval = write_block_size(data->tdb,
297 						  data->tdb_data_size);
298 			if (retval) {
299 				tdb_transaction_cancel(data->tdb);
300 				retval = EXT2_ET_TDB_ERR_IO;
301 				free(read_ptr);
302 				return retval;
303 			}
304 		}
305 		retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
306 		if (retval == -1) {
307 			/*
308 			 * TDB_ERR_EXISTS cannot happen because we
309 			 * have already verified it doesn't exist
310 			 */
311 			tdb_transaction_cancel(data->tdb);
312 			retval = EXT2_ET_TDB_ERR_IO;
313 			free(read_ptr);
314 			return retval;
315 		}
316 		free(read_ptr);
317 		/* Next block */
318 		block_num++;
319 	}
320 	tdb_transaction_commit(data->tdb);
321 
322 	return retval;
323 }
324 
undo_io_read_error(io_channel channel ATTR ((unused)),unsigned long block ATTR ((unused)),int count ATTR ((unused)),void * data ATTR ((unused)),size_t size ATTR ((unused)),int actual,errcode_t error ATTR ((unused)))325 static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
326 				    unsigned long block ATTR((unused)),
327 				    int count ATTR((unused)),
328 				    void *data ATTR((unused)),
329 				    size_t size ATTR((unused)),
330 				    int actual,
331 				    errcode_t error ATTR((unused)))
332 {
333 	actual_size = actual;
334 	return error;
335 }
336 
undo_err_handler_init(io_channel channel)337 static void undo_err_handler_init(io_channel channel)
338 {
339 	channel->read_error = undo_io_read_error;
340 }
341 
undo_open(const char * name,int flags,io_channel * channel)342 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
343 {
344 	io_channel	io = NULL;
345 	struct undo_private_data *data = NULL;
346 	errcode_t	retval;
347 
348 	if (name == 0)
349 		return EXT2_ET_BAD_DEVICE_NAME;
350 	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
351 	if (retval)
352 		return retval;
353 	memset(io, 0, sizeof(struct struct_io_channel));
354 	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
355 	retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
356 	if (retval)
357 		goto cleanup;
358 
359 	io->manager = undo_io_manager;
360 	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
361 	if (retval)
362 		goto cleanup;
363 
364 	strcpy(io->name, name);
365 	io->private_data = data;
366 	io->block_size = 1024;
367 	io->read_error = 0;
368 	io->write_error = 0;
369 	io->refcount = 1;
370 
371 	memset(data, 0, sizeof(struct undo_private_data));
372 	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
373 
374 	if (undo_io_backing_manager) {
375 		retval = undo_io_backing_manager->open(name, flags,
376 						       &data->real);
377 		if (retval)
378 			goto cleanup;
379 	} else {
380 		data->real = 0;
381 	}
382 
383 	/* setup the tdb file */
384 	data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
385 			     O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
386 	if (!data->tdb) {
387 		retval = errno;
388 		goto cleanup;
389 	}
390 
391 	/*
392 	 * setup err handler for read so that we know
393 	 * when the backing manager fails do short read
394 	 */
395 	undo_err_handler_init(data->real);
396 
397 	*channel = io;
398 	return 0;
399 
400 cleanup:
401 	if (data->real)
402 		io_channel_close(data->real);
403 	if (data)
404 		ext2fs_free_mem(&data);
405 	if (io)
406 		ext2fs_free_mem(&io);
407 	return retval;
408 }
409 
undo_close(io_channel channel)410 static errcode_t undo_close(io_channel channel)
411 {
412 	struct undo_private_data *data;
413 	errcode_t	retval = 0;
414 
415 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
416 	data = (struct undo_private_data *) channel->private_data;
417 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
418 
419 	if (--channel->refcount > 0)
420 		return 0;
421 	/* Before closing write the file system identity */
422 	retval = write_file_system_identity(channel, data->tdb);
423 	if (retval)
424 		return retval;
425 	if (data->real)
426 		retval = io_channel_close(data->real);
427 	if (data->tdb)
428 		tdb_close(data->tdb);
429 	ext2fs_free_mem(&channel->private_data);
430 	if (channel->name)
431 		ext2fs_free_mem(&channel->name);
432 	ext2fs_free_mem(&channel);
433 
434 	return retval;
435 }
436 
undo_set_blksize(io_channel channel,int blksize)437 static errcode_t undo_set_blksize(io_channel channel, int blksize)
438 {
439 	struct undo_private_data *data;
440 	errcode_t		retval = 0;
441 
442 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
443 	data = (struct undo_private_data *) channel->private_data;
444 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
445 
446 	if (data->real)
447 		retval = io_channel_set_blksize(data->real, blksize);
448 	/*
449 	 * Set the block size used for tdb
450 	 */
451 	if (!data->tdb_data_size) {
452 		data->tdb_data_size = blksize;
453 	}
454 	channel->block_size = blksize;
455 	return retval;
456 }
457 
undo_read_blk(io_channel channel,unsigned long block,int count,void * buf)458 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
459 			       int count, void *buf)
460 {
461 	errcode_t	retval = 0;
462 	struct undo_private_data *data;
463 
464 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
465 	data = (struct undo_private_data *) channel->private_data;
466 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
467 
468 	if (data->real)
469 		retval = io_channel_read_blk(data->real, block, count, buf);
470 
471 	return retval;
472 }
473 
undo_write_blk(io_channel channel,unsigned long block,int count,const void * buf)474 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
475 				int count, const void *buf)
476 {
477 	struct undo_private_data *data;
478 	errcode_t	retval = 0;
479 
480 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
481 	data = (struct undo_private_data *) channel->private_data;
482 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
483 	/*
484 	 * First write the existing content into database
485 	 */
486 	retval = undo_write_tdb(channel, block, count);
487 	if (retval)
488 		 return retval;
489 	if (data->real)
490 		retval = io_channel_write_blk(data->real, block, count, buf);
491 
492 	return retval;
493 }
494 
undo_write_byte(io_channel channel,unsigned long offset,int size,const void * buf)495 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
496 				 int size, const void *buf)
497 {
498 	struct undo_private_data *data;
499 	errcode_t	retval = 0;
500 	ext2_loff_t	location;
501 	unsigned long blk_num, count;;
502 
503 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
504 	data = (struct undo_private_data *) channel->private_data;
505 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
506 
507 	location = offset + data->offset;
508 	blk_num = location/channel->block_size;
509 	/*
510 	 * the size specified may spread across multiple blocks
511 	 * also make sure we account for the fact that block start
512 	 * offset for tdb is different from the backing I/O manager
513 	 * due to possible different block size
514 	 */
515 	count = (size + (location % channel->block_size) +
516 			channel->block_size  -1)/channel->block_size;
517 	retval = undo_write_tdb(channel, blk_num, count);
518 	if (retval)
519 		return retval;
520 	if (data->real && data->real->manager->write_byte)
521 		retval = io_channel_write_byte(data->real, offset, size, buf);
522 
523 	return retval;
524 }
525 
526 /*
527  * Flush data buffers to disk.
528  */
undo_flush(io_channel channel)529 static errcode_t undo_flush(io_channel channel)
530 {
531 	errcode_t	retval = 0;
532 	struct undo_private_data *data;
533 
534 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
535 	data = (struct undo_private_data *) channel->private_data;
536 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
537 
538 	if (data->real)
539 		retval = io_channel_flush(data->real);
540 
541 	return retval;
542 }
543 
undo_set_option(io_channel channel,const char * option,const char * arg)544 static errcode_t undo_set_option(io_channel channel, const char *option,
545 				 const char *arg)
546 {
547 	errcode_t	retval = 0;
548 	struct undo_private_data *data;
549 	unsigned long tmp;
550 	char *end;
551 
552 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
553 	data = (struct undo_private_data *) channel->private_data;
554 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
555 
556 	if (!strcmp(option, "tdb_data_size")) {
557 		if (!arg)
558 			return EXT2_ET_INVALID_ARGUMENT;
559 
560 		tmp = strtoul(arg, &end, 0);
561 		if (*end)
562 			return EXT2_ET_INVALID_ARGUMENT;
563 		if (!data->tdb_data_size || !data->tdb_written) {
564 			data->tdb_data_size = tmp;
565 		}
566 		return 0;
567 	}
568 	/*
569 	 * Need to support offset option to work with
570 	 * Unix I/O manager
571 	 */
572 	if (data->real && data->real->manager->set_option) {
573 		retval = data->real->manager->set_option(data->real,
574 							option, arg);
575 	}
576 	if (!retval && !strcmp(option, "offset")) {
577 		if (!arg)
578 			return EXT2_ET_INVALID_ARGUMENT;
579 
580 		tmp = strtoul(arg, &end, 0);
581 		if (*end)
582 			return EXT2_ET_INVALID_ARGUMENT;
583 		data->offset = tmp;
584 	}
585 	return retval;
586 }
587