source: trunk/minix/commands/de/de_recover.c@ 21

Last change on this file since 21 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 12.8 KB
RevLine 
[9]1/****************************************************************/
2/* */
3/* de_recover.c */
4/* */
5/* File restoration routines. */
6/* */
7/****************************************************************/
8/* origination 1989-Jan-21 Terrence W. Holm */
9/* handle "holes" 1989-Jan-28 Terrence W. Holm */
10/****************************************************************/
11
12
13#include <minix/config.h>
14#include <sys/types.h>
15#include <sys/dir.h>
16#include <sys/stat.h>
17#include <fcntl.h>
18#include <limits.h>
19#include <pwd.h>
20#include <string.h>
21#include <unistd.h>
22#include <dirent.h>
23
24#include <minix/const.h>
25#include <minix/type.h>
26#include "../../servers/fs/const.h"
27#include "../../servers/fs/type.h"
28#include "../../servers/fs/inode.h"
29#include <minix/fslib.h>
30
31#include "de.h"
32
33_PROTOTYPE(int Indirect, (de_state *s, zone_t block, off_t *size, int dblind));
34_PROTOTYPE(int Data_Block, (de_state *s, zone_t block, off_t *file_size ));
35_PROTOTYPE(int Free_Block, (de_state *s, zone_t block ));
36
37
38
39
40/****************************************************************/
41/* */
42/* Path_Dir_File( path_name, dir_name, file_name ) */
43/* */
44/* Split "path_name" into a directory name and */
45/* a file name. */
46/* */
47/* Zero is returned on error conditions. */
48/* */
49/****************************************************************/
50
51
52int Path_Dir_File( path_name, dir_name, file_name )
53 char *path_name;
54 char **dir_name;
55 char **file_name;
56
57 {
58 char *p;
59 static char directory[ MAX_STRING + 1 ];
60 static char filename[ MAX_STRING + 1 ];
61
62
63 if ( (p = strrchr( path_name, '/' )) == NULL )
64 {
65 strcpy( directory, "." );
66 strcpy( filename, path_name );
67 }
68 else
69 {
70 *directory = '\0';
71 strncat( directory, path_name, p - path_name );
72 strcpy( filename, p + 1 );
73 }
74
75 if ( *directory == '\0' )
76 strcpy( directory, "/" );
77
78 if ( *filename == '\0' )
79 {
80 Warning( "A file name must follow the directory name" );
81 return( 0 );
82 }
83
84 *dir_name = directory;
85 *file_name = filename;
86
87 return( 1 );
88 }
89
90
91
92
93
94
95/****************************************************************/
96/* */
97/* File_Device( file_name ) */
98/* */
99/* Return the name of the file system device */
100/* containing the file "file_name". */
101/* */
102/* This is used if the "-r" option was specified. */
103/* In this case we have only been given a file */
104/* name, and must determine which file system */
105/* device to open. */
106/* */
107/* NULL is returned on error conditions. */
108/* */
109/****************************************************************/
110
111
112
113char *File_Device( file_name )
114 char *file_name;
115
116 {
117 struct stat file_stat;
118 struct stat device_stat;
119 int dev_d;
120 struct direct entry;
121 static char device_name[ NAME_MAX + 1 ];
122
123
124 if ( access( file_name, R_OK ) != 0 )
125 {
126 Warning( "Can not find %s", file_name );
127 return( NULL );
128 }
129
130
131 if ( stat( file_name, &file_stat ) == -1 )
132 {
133 Warning( "Can not stat(2) %s", file_name );
134 return( NULL );
135 }
136
137
138 /* Open /dev for reading */
139
140 if ( (dev_d = open( DEV, O_RDONLY )) == -1 )
141 {
142 Warning( "Can not read %s", DEV );
143 return( NULL );
144 }
145
146
147 while ( read( dev_d, (char *) &entry, sizeof(struct direct) )
148 == sizeof(struct direct) )
149 {
150 if ( entry.d_ino == 0 )
151 continue;
152
153 strcpy( device_name, DEV );
154 strcat( device_name, "/" );
155 strncat( device_name, entry.d_name, NAME_MAX );
156
157 if ( stat( device_name, &device_stat ) == -1 )
158 continue;
159
160 if ( (device_stat.st_mode & S_IFMT) != S_IFBLK )
161 continue;
162
163 if ( file_stat.st_dev == device_stat.st_rdev )
164 {
165 close( dev_d );
166 return( device_name );
167 }
168 }
169
170 close( dev_d );
171
172 Warning( "The device containing file %s is not in %s", file_name, DEV );
173
174 return( NULL );
175 }
176
177
178
179
180
181
182/****************************************************************/
183/* */
184/* Find_Deleted_Entry( state, path_name ) */
185/* */
186/* Split "path_name" into a directory name and */
187/* a file name. Then search the directory for */
188/* an entry that would match the deleted file */
189/* name. (Deleted entries have a zero i-node */
190/* number, but the original i-node number is */
191/* placed at the end of the file name.) */
192/* */
193/* If successful an i-node number is returned, */
194/* else zero is returned. */
195/* */
196/****************************************************************/
197
198
199ino_t Find_Deleted_Entry( s, path_name )
200 de_state *s;
201 char *path_name;
202
203 {
204 char *dir_name;
205 char *file_name;
206
207
208 /* Check if the file exists */
209
210 if ( access( path_name, F_OK ) == 0 )
211 {
212 Warning( "File has not been deleted" );
213 return( 0 );
214 }
215
216
217 /* Split the path name into a directory and a file name */
218
219 if ( ! Path_Dir_File( path_name, &dir_name, &file_name ) )
220 return( 0 );
221
222
223 /* Check to make sure the user has read permission on */
224 /* the directory. */
225
226 if ( access( dir_name, R_OK ) != 0 )
227 {
228 Warning( "Can not find %s", dir_name );
229 return( 0 );
230 }
231
232
233 /* Make sure "dir_name" is really a directory. */
234 {
235 struct stat dir_stat;
236
237 if ( stat( dir_name, &dir_stat ) == -1 ||
238 (dir_stat.st_mode & S_IFMT) != S_IFDIR )
239 {
240 Warning( "Can not find directory %s", dir_name );
241 return( 0 );
242 }
243 }
244
245
246 /* Make sure the directory is on the current */
247 /* file system device. */
248
249 if ( Find_Inode( s, dir_name ) == 0 )
250 return( 0 );
251
252
253 /* Open the directory and search for the lost file name. */
254 {
255 int dir_d;
256 int count;
257 struct direct entry;
258
259 if ( (dir_d = open( dir_name, O_RDONLY )) == -1 )
260 {
261 Warning( "Can not read directory %s", dir_name );
262 return( 0 );
263 }
264
265 while ( (count = read( dir_d, (char *) &entry, sizeof(struct direct) ))
266 == sizeof(struct direct) )
267 {
268 if ( entry.d_ino == 0 &&
269 strncmp( file_name, entry.d_name, NAME_MAX - sizeof(ino_t) ) == 0 )
270 {
271 ino_t inode = *( (ino_t *) &entry.d_name[ NAME_MAX - sizeof(ino_t) ] );
272
273 close( dir_d );
274
275 if ( inode < 1 || inode > s->inodes )
276 {
277 Warning( "Illegal i-node number" );
278 return( 0 );
279 }
280
281 return( inode );
282 }
283 }
284
285 close( dir_d );
286
287 if ( count == 0 )
288 Warning( "Can not find a deleted entry for %s", file_name );
289 else
290 Warning( "Problem reading directory %s", dir_name );
291
292 return( 0 );
293 }
294 }
295
296
297
298
299
300
301/****************************************************************/
302/* */
303/* Recover_Blocks( state ) */
304/* */
305/* Try to recover all the blocks for the i-node */
306/* currently pointed to by "s->address". The */
307/* i-node and all of the blocks must be marked */
308/* as FREE in the bit maps. The owner of the */
309/* i-node must match the current real user name. */
310/* */
311/* "Holes" in the original file are maintained. */
312/* This allows moving sparse files from one device */
313/* to another. */
314/* */
315/* On any error -1L is returned, otherwise the */
316/* size of the recovered file is returned. */
317/* */
318/* */
319/* NOTE: Once a user has read access to a device, */
320/* there is a security hole, as we lose the */
321/* normal file system protection. For convenience, */
322/* de(1) is sometimes set-uid root, this allows */
323/* anyone to use the "-r" option. When recovering, */
324/* Recover_Blocks() can only superficially check */
325/* the validity of a request. */
326/* */
327/****************************************************************/
328
329
330off_t Recover_Blocks( s )
331 de_state *s;
332
333 {
334 struct inode core_inode;
335 d1_inode *dip1;
336 d2_inode *dip2;
337 struct inode *inode = &core_inode;
338 bit_t node = (s->address - (s->first_data - s->inode_blocks) * K) /
339 s->inode_size + 1;
340
341 dip1 = (d1_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ];
342 dip2 = (d2_inode *) &s->buffer[ s->offset & ~ PAGE_MASK
343 & ~ (V2_INODE_SIZE-1) ];
344 conv_inode( inode, dip1, dip2, READING, s->magic );
345
346 if ( s->block < s->first_data - s->inode_blocks ||
347 s->block >= s->first_data )
348 {
349 Warning( "Not in an inode block" );
350 return( -1L );
351 }
352
353
354 /* Is this a valid, but free i-node? */
355
356 if ( node > s->inodes )
357 {
358 Warning( "Not an inode" );
359 return( -1L );
360 }
361
362 if ( In_Use(node, s->inode_map) )
363 {
364 Warning( "I-node is in use" );
365 return( -1L );
366 }
367
368
369 /* Only recover files that belonged to the real user. */
370
371 {
372 uid_t real_uid = getuid();
373 struct passwd *user = getpwuid( real_uid );
374
375 if ( real_uid != SU_UID && real_uid != inode->i_uid )
376 {
377 Warning( "I-node did not belong to user %s", user ? user->pw_name : "" );
378 return( -1L );
379 }
380 }
381
382
383 /* Recover all the blocks of the file. */
384
385 {
386 off_t file_size = inode->i_size;
387 int i;
388
389
390 /* Up to s->ndzones pointers are stored in the i-node. */
391
392 for ( i = 0; i < s->ndzones; ++i )
393 {
394 if ( file_size == 0 )
395 return( inode->i_size );
396
397 if ( ! Data_Block( s, inode->i_zone[ i ], &file_size ) )
398 return( -1L );
399 }
400
401 if ( file_size == 0 )
402 return( inode->i_size );
403
404
405 /* An indirect block can contain up to inode->i_indirects more blk ptrs. */
406
407 if ( ! Indirect( s, inode->i_zone[ s->ndzones ], &file_size, 0 ) )
408 return( -1L );
409
410 if ( file_size == 0 )
411 return( inode->i_size );
412
413
414 /* A double indirect block can contain up to inode->i_indirects blk ptrs. */
415
416 if ( ! Indirect( s, inode->i_zone[ s->ndzones+1 ], &file_size, 1 ) )
417 return( -1L );
418
419 if ( file_size == 0 )
420 return( inode->i_size );
421
422 Error( "Internal fault (file_size != 0)" );
423 }
424
425 /* NOTREACHED */
426 return( -1L );
427 }
428
429
430
431
432
433
434/* Indirect( state, block, &file_size, double )
435 *
436 * Recover all the blocks pointed to by the indirect block
437 * "block", up to "file_size" bytes. If "double" is true,
438 * then "block" is a double-indirect block pointing to
439 * V*_INDIRECTS indirect blocks.
440 *
441 * If a "hole" is encountered, then just seek ahead in the
442 * output file.
443 */
444
445
446int Indirect( s, block, file_size, dblind )
447 de_state *s;
448 zone_t block;
449 off_t *file_size;
450 int dblind;
451
452 {
453 union
454 {
455 zone1_t ind1[ V1_INDIRECTS ];
456 zone_t ind2[ V2_INDIRECTS(_MAX_BLOCK_SIZE) ];
457 } indirect;
458 int i;
459 zone_t zone;
460
461 /* Check for a "hole". */
462
463 if ( block == NO_ZONE )
464 {
465 off_t skip = (off_t) s->nr_indirects * K;
466
467 if ( *file_size < skip || dblind )
468 {
469 Warning( "File has a hole at the end" );
470 return( 0 );
471 }
472
473 if ( fseek( s->file_f, skip, SEEK_CUR ) == -1 )
474 {
475 Warning( "Problem seeking %s", s->file_name );
476 return( 0 );
477 }
478
479 *file_size -= skip;
480 return( 1 );
481 }
482
483
484 /* Not a "hole". Recover indirect block, if not in use. */
485
486 if ( ! Free_Block( s, block ) )
487 return( 0 );
488
489
490 Read_Disk( s, (long) block << K_SHIFT, (char *) &indirect );
491
492 for ( i = 0; i < s->nr_indirects; ++i )
493 {
494 if ( *file_size == 0 )
495 return( 1 );
496
497 zone = (s->v1 ? indirect.ind1[ i ] : indirect.ind2[ i ]);
498 if ( dblind )
499 {
500 if ( ! Indirect( s, zone, file_size, 0 ) )
501 return( 0 );
502 }
503 else
504 {
505 if ( ! Data_Block( s, zone, file_size ) )
506 return( 0 );
507 }
508 }
509
510 return( 1 );
511 }
512
513
514
515
516
517
518/* Data_Block( state, block, &file_size )
519 *
520 * If "block" is free then write Min(file_size, k)
521 * bytes from it onto the current output file.
522 *
523 * If "block" is zero, this means that a 1k "hole"
524 * is in the file. The recovered file maintains
525 * the reduced size by not allocating the block.
526 *
527 * The file size is decremented accordingly.
528 */
529
530
531int Data_Block( s, block, file_size )
532 de_state *s;
533 zone_t block;
534 off_t *file_size;
535
536 {
537 char buffer[ K ];
538 off_t block_size = *file_size > K ? K : *file_size;
539
540
541 /* Check for a "hole". */
542
543 if ( block == NO_ZONE )
544 {
545 if ( block_size < K )
546 {
547 Warning( "File has a hole at the end" );
548 return( 0 );
549 }
550
551 if ( fseek( s->file_f, block_size, SEEK_CUR ) == -1 )
552 {
553 Warning( "Problem seeking %s", s->file_name );
554 return( 0 );
555 }
556
557 *file_size -= block_size;
558 return( 1 );
559 }
560
561
562 /* Block is not a "hole". Copy it to output file, if not in use. */
563
564 if ( ! Free_Block( s, block ) )
565 return( 0 );
566
567 Read_Disk( s, (long) block << K_SHIFT, buffer );
568
569
570 if ( fwrite( buffer, 1, (size_t) block_size, s->file_f )
571 != (size_t) block_size )
572 {
573 Warning( "Problem writing %s", s->file_name );
574 return( 0 );
575 }
576
577 *file_size -= block_size;
578 return( 1 );
579 }
580
581
582
583
584
585
586/* Free_Block( state, block )
587 *
588 * Make sure "block" is a valid data block number, and it
589 * has not been allocated to another file.
590 */
591
592
593int Free_Block( s, block )
594 de_state *s;
595 zone_t block;
596
597 {
598 if ( block < s->first_data || block >= s->zones )
599 {
600 Warning( "Illegal block number" );
601 return( 0 );
602 }
603
604 if ( In_Use( (bit_t) (block - (s->first_data - 1)), s->zone_map ) )
605 {
606 Warning( "Encountered an \"in use\" data block" );
607 return( 0 );
608 }
609
610 return( 1 );
611 }
612
Note: See TracBrowser for help on using the repository browser.