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