1 /****************************************************************/
5 /* File restoration routines. */
7 /****************************************************************/
8 /* origination 1989-Jan-21 Terrence W. Holm */
9 /* handle "holes" 1989-Jan-28 Terrence W. Holm */
10 /****************************************************************/
13 #include <minix/config.h>
14 #include <sys/types.h>
24 #include <minix/const.h>
25 #include <minix/type.h>
26 #include "../../servers/mfs/const.h"
27 #include "../../servers/mfs/type.h"
28 #include "../../servers/mfs/inode.h"
29 #include <minix/fslib.h>
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
));
40 /****************************************************************/
42 /* Path_Dir_File( path_name, dir_name, file_name ) */
44 /* Split "path_name" into a directory name and */
47 /* Zero is returned on error conditions. */
49 /****************************************************************/
52 int Path_Dir_File( path_name
, dir_name
, file_name
)
59 static char directory
[ MAX_STRING
+ 1 ];
60 static char filename
[ MAX_STRING
+ 1 ];
63 if ( (p
= strrchr( path_name
, '/' )) == NULL
)
65 strcpy( directory
, "." );
66 strcpy( filename
, path_name
);
71 strncat( directory
, path_name
, p
- path_name
);
72 strcpy( filename
, p
+ 1 );
75 if ( *directory
== '\0' )
76 strcpy( directory
, "/" );
78 if ( *filename
== '\0' )
80 Warning( "A file name must follow the directory name" );
84 *dir_name
= directory
;
85 *file_name
= filename
;
95 /****************************************************************/
97 /* File_Device( file_name ) */
99 /* Return the name of the file system device */
100 /* containing the file "file_name". */
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. */
107 /* NULL is returned on error conditions. */
109 /****************************************************************/
113 char *File_Device( file_name
)
117 struct stat file_stat
;
118 struct stat device_stat
;
121 static char device_name
[ NAME_MAX
+ 1 ];
124 if ( access( file_name
, R_OK
) != 0 )
126 Warning( "Can not find %s", file_name
);
131 if ( stat( file_name
, &file_stat
) == -1 )
133 Warning( "Can not stat(2) %s", file_name
);
138 /* Open /dev for reading */
140 if ( (dev_d
= open( DEV
, O_RDONLY
)) == -1 )
142 Warning( "Can not read %s", DEV
);
147 while ( read( dev_d
, (char *) &entry
, sizeof(struct direct
) )
148 == sizeof(struct direct
) )
150 if ( entry
.d_ino
== 0 )
153 strcpy( device_name
, DEV
);
154 strcat( device_name
, "/" );
155 strncat( device_name
, entry
.d_name
, NAME_MAX
);
157 if ( stat( device_name
, &device_stat
) == -1 )
160 if ( (device_stat
.st_mode
& S_IFMT
) != S_IFBLK
)
163 if ( file_stat
.st_dev
== device_stat
.st_rdev
)
166 return( device_name
);
172 Warning( "The device containing file %s is not in %s", file_name
, DEV
);
182 /****************************************************************/
184 /* Find_Deleted_Entry( state, path_name ) */
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.) */
193 /* If successful an i-node number is returned, */
194 /* else zero is returned. */
196 /****************************************************************/
199 ino_t
Find_Deleted_Entry( s
, path_name
)
208 /* Check if the file exists */
210 if ( access( path_name
, F_OK
) == 0 )
212 Warning( "File has not been deleted" );
217 /* Split the path name into a directory and a file name */
219 if ( ! Path_Dir_File( path_name
, &dir_name
, &file_name
) )
223 /* Check to make sure the user has read permission on */
226 if ( access( dir_name
, R_OK
) != 0 )
228 Warning( "Can not find %s", dir_name
);
233 /* Make sure "dir_name" is really a directory. */
235 struct stat dir_stat
;
237 if ( stat( dir_name
, &dir_stat
) == -1 ||
238 (dir_stat
.st_mode
& S_IFMT
) != S_IFDIR
)
240 Warning( "Can not find directory %s", dir_name
);
246 /* Make sure the directory is on the current */
247 /* file system device. */
249 if ( Find_Inode( s
, dir_name
) == 0 )
253 /* Open the directory and search for the lost file name. */
259 if ( (dir_d
= open( dir_name
, O_RDONLY
)) == -1 )
261 Warning( "Can not read directory %s", dir_name
);
265 while ( (count
= read( dir_d
, (char *) &entry
, sizeof(struct direct
) ))
266 == sizeof(struct direct
) )
268 if ( entry
.d_ino
== 0 &&
269 strncmp( file_name
, entry
.d_name
, NAME_MAX
- sizeof(ino_t
) ) == 0 )
271 ino_t inode
= *( (ino_t
*) &entry
.d_name
[ NAME_MAX
- sizeof(ino_t
) ] );
275 if ( inode
< 1 || inode
> s
->inodes
)
277 Warning( "Illegal i-node number" );
288 Warning( "Can not find a deleted entry for %s", file_name
);
290 Warning( "Problem reading directory %s", dir_name
);
301 /****************************************************************/
303 /* Recover_Blocks( state ) */
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. */
311 /* "Holes" in the original file are maintained. */
312 /* This allows moving sparse files from one device */
315 /* On any error -1L is returned, otherwise the */
316 /* size of the recovered file is returned. */
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. */
327 /****************************************************************/
330 off_t
Recover_Blocks( s
)
334 struct inode core_inode
;
337 struct inode
*inode
= &core_inode
;
338 bit_t node
= (s
->address
- (s
->first_data
- s
->inode_blocks
) * K
) /
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
);
346 if ( s
->block
< s
->first_data
- s
->inode_blocks
||
347 s
->block
>= s
->first_data
)
349 Warning( "Not in an inode block" );
354 /* Is this a valid, but free i-node? */
356 if ( node
> s
->inodes
)
358 Warning( "Not an inode" );
362 if ( In_Use(node
, s
->inode_map
) )
364 Warning( "I-node is in use" );
369 /* Only recover files that belonged to the real user. */
372 uid_t real_uid
= getuid();
373 struct passwd
*user
= getpwuid( real_uid
);
375 if ( real_uid
!= SU_UID
&& real_uid
!= inode
->i_uid
)
377 Warning( "I-node did not belong to user %s", user
? user
->pw_name
: "" );
383 /* Recover all the blocks of the file. */
386 off_t file_size
= inode
->i_size
;
390 /* Up to s->ndzones pointers are stored in the i-node. */
392 for ( i
= 0; i
< s
->ndzones
; ++i
)
394 if ( file_size
== 0 )
395 return( inode
->i_size
);
397 if ( ! Data_Block( s
, inode
->i_zone
[ i
], &file_size
) )
401 if ( file_size
== 0 )
402 return( inode
->i_size
);
405 /* An indirect block can contain up to inode->i_indirects more blk ptrs. */
407 if ( ! Indirect( s
, inode
->i_zone
[ s
->ndzones
], &file_size
, 0 ) )
410 if ( file_size
== 0 )
411 return( inode
->i_size
);
414 /* A double indirect block can contain up to inode->i_indirects blk ptrs. */
416 if ( ! Indirect( s
, inode
->i_zone
[ s
->ndzones
+1 ], &file_size
, 1 ) )
419 if ( file_size
== 0 )
420 return( inode
->i_size
);
422 Error( "Internal fault (file_size != 0)" );
434 /* Indirect( state, block, &file_size, double )
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.
441 * If a "hole" is encountered, then just seek ahead in the
446 int Indirect( s
, block
, file_size
, dblind
)
455 zone1_t ind1
[ V1_INDIRECTS
];
456 zone_t ind2
[ V2_INDIRECTS(_MAX_BLOCK_SIZE
) ];
461 /* Check for a "hole". */
463 if ( block
== NO_ZONE
)
465 off_t skip
= (off_t
) s
->nr_indirects
* K
;
467 if ( *file_size
< skip
|| dblind
)
469 Warning( "File has a hole at the end" );
473 if ( fseek( s
->file_f
, skip
, SEEK_CUR
) == -1 )
475 Warning( "Problem seeking %s", s
->file_name
);
484 /* Not a "hole". Recover indirect block, if not in use. */
486 if ( ! Free_Block( s
, block
) )
490 Read_Disk( s
, (long) block
<< K_SHIFT
, (char *) &indirect
);
492 for ( i
= 0; i
< s
->nr_indirects
; ++i
)
494 if ( *file_size
== 0 )
497 zone
= (s
->v1
? indirect
.ind1
[ i
] : indirect
.ind2
[ i
]);
500 if ( ! Indirect( s
, zone
, file_size
, 0 ) )
505 if ( ! Data_Block( s
, zone
, file_size
) )
518 /* Data_Block( state, block, &file_size )
520 * If "block" is free then write Min(file_size, k)
521 * bytes from it onto the current output file.
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.
527 * The file size is decremented accordingly.
531 int Data_Block( s
, block
, file_size
)
538 off_t block_size
= *file_size
> K
? K
: *file_size
;
541 /* Check for a "hole". */
543 if ( block
== NO_ZONE
)
545 if ( block_size
< K
)
547 Warning( "File has a hole at the end" );
551 if ( fseek( s
->file_f
, block_size
, SEEK_CUR
) == -1 )
553 Warning( "Problem seeking %s", s
->file_name
);
557 *file_size
-= block_size
;
562 /* Block is not a "hole". Copy it to output file, if not in use. */
564 if ( ! Free_Block( s
, block
) )
567 Read_Disk( s
, (long) block
<< K_SHIFT
, buffer
);
570 if ( fwrite( buffer
, 1, (size_t) block_size
, s
->file_f
)
571 != (size_t) block_size
)
573 Warning( "Problem writing %s", s
->file_name
);
577 *file_size
-= block_size
;
586 /* Free_Block( state, block )
588 * Make sure "block" is a valid data block number, and it
589 * has not been allocated to another file.
593 int Free_Block( s
, block
)
598 if ( block
< s
->first_data
|| block
>= s
->zones
)
600 Warning( "Illegal block number" );
604 if ( In_Use( (bit_t
) (block
- (s
->first_data
- 1)), s
->zone_map
) )
606 Warning( "Encountered an \"in use\" data block" );