1062 lines
27 KiB
C
1062 lines
27 KiB
C
/************************************************************************
|
||
* *
|
||
* Copyright (c) 1984, Fred Fish *
|
||
* All Rights Reserved *
|
||
* *
|
||
* This software and/or documentation is protected by U.S. *
|
||
* Copyright Law (Title 17 United States Code). Unauthorized *
|
||
* reproduction and/or sales may result in imprisonment of up *
|
||
* to 1 year and fines of up to $10,000 (17 USC 506). *
|
||
* Copyright infringers may also be subject to civil liability. *
|
||
* *
|
||
************************************************************************
|
||
*/
|
||
|
||
|
||
/*
|
||
* FILE
|
||
*
|
||
* extract.c contains routines specific to extracting files
|
||
*
|
||
* SCCS
|
||
*
|
||
* @(#)extract.c 9.11 5/11/88
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Contains routines specific to extracting files from bru archives.
|
||
*
|
||
*/
|
||
|
||
|
||
#include "autoconfig.h"
|
||
|
||
#include <stdio.h>
|
||
|
||
#if (unix || xenix)
|
||
# include <sys/types.h>
|
||
# include <sys/stat.h>
|
||
#else
|
||
# include "sys.h"
|
||
#endif
|
||
|
||
#include "typedefs.h"
|
||
#include "dbug.h"
|
||
#include "manifest.h"
|
||
#include "config.h"
|
||
#include "errors.h"
|
||
#include "blocks.h"
|
||
#include "macros.h"
|
||
#include "trees.h"
|
||
#include "finfo.h"
|
||
#include "flags.h"
|
||
#include "bruinfo.h"
|
||
|
||
#define DIR_MAGIC 0777 /* Default permissions for directories */
|
||
/* This is modified by umask */
|
||
dev_t saved_st_dev = 0;
|
||
|
||
|
||
/*
|
||
* External bru functions.
|
||
*/
|
||
|
||
extern BOOLEAN decompfip (); /* Decompress a file, saving to destination */
|
||
extern BOOLEAN openzfile (); /* Open a temp file for compressed file */
|
||
extern int setinfo ();
|
||
extern int s_mknod (); /* Make a directory, special, or other file */
|
||
extern BOOLEAN confirmed (); /* Confirm action */
|
||
extern union blk *ar_next (); /* Get pointer to next archive block */
|
||
extern VOID bru_error (); /* Report an error to user */
|
||
extern VOID ar_close (); /* Flush buffers and close the archive */
|
||
extern VOID ar_open (); /* Open the archive */
|
||
extern VOID ar_read (); /* Read archive block */
|
||
extern VOID reload (); /* Reload first volume for rescan */
|
||
extern VOID done (); /* Finish up and exit */
|
||
extern BOOLEAN newdir (); /* Make a directory */
|
||
extern BOOLEAN mklink (); /* Make a link to a file */
|
||
extern BOOLEAN mksymlink (); /* Make a symbolic link to a file */
|
||
extern VOID scan (); /* Scan archive */
|
||
extern VOID verbosity (); /* Issue verbosity message */
|
||
extern BOOLEAN selected (); /* Apply selection criteria */
|
||
extern VOID finfo_init (); /* Initialize a finfo structure */
|
||
extern BOOLEAN file_access (); /* Check file for access */
|
||
extern BOOLEAN dir_access (); /* Check parent for access */
|
||
extern BOOLEAN out_of_date (); /* Existing file out of date */
|
||
extern BOOLEAN unconditional(); /* Unconditionally overwrite */
|
||
extern char *s_strrchr (); /* Find last given character in string */
|
||
extern int s_close (); /* Invoke library file close function */
|
||
extern int s_umask (); /* Invoke umask library function */
|
||
extern int s_creat (); /* Create a file */
|
||
extern VOID file_chown (); /* Change file owner and group */
|
||
extern int s_utime (); /* Set file access/modification times */
|
||
extern int s_chmod (); /* Change mode of a file */
|
||
extern int s_write (); /* Write data to file */
|
||
extern int s_unlink (); /* Remove a directory entry */
|
||
extern long s_time (); /* Get current time */
|
||
extern VOID mark_archived (); /* Set the "has been archived" bit */
|
||
extern VOID clear_archived (); /* Clear the "has been archived" bit */
|
||
|
||
extern char *getlinkname();
|
||
extern char *add_link();
|
||
|
||
/*
|
||
* External bru variables.
|
||
*/
|
||
|
||
extern struct bru_info info; /* Current invocation information */
|
||
extern struct cmd_flags flags; /* Command line flags */
|
||
extern char mode; /* Current mode (citdxh) */
|
||
|
||
/*
|
||
* Local functions.
|
||
*/
|
||
|
||
static VOID do_extract (); /* Actually do the extraction */
|
||
static VOID xfile (); /* Extract the file */
|
||
static VOID makelink (); /* Make link to existing file */
|
||
static BOOLEAN makedir (); /* Make a directory */
|
||
static BOOLEAN makeparent (); /* Make a parent directory */
|
||
static VOID makestat (); /* Make default stat buffer */
|
||
static VOID makespecial (); /* Make a special file */
|
||
static VOID xregfile (); /* Do extraction of regular file */
|
||
static VOID makefile (); /* Make first copy of file */
|
||
static VOID attributes (); /* Set attributes to match */
|
||
|
||
/*
|
||
* Stuff for signal handling.
|
||
*/
|
||
|
||
extern VOID sig_catch (); /* Set signal catch state */
|
||
extern VOID sig_push (); /* Push signal state */
|
||
extern VOID sig_pop (); /* Pop signal state */
|
||
extern BOOLEAN interrupt; /* Interrupt received */
|
||
|
||
|
||
/*
|
||
* NAME
|
||
*
|
||
* extract main entry point for extraction of file from archive
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* VOID extract ()
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* This is the main entry point for extracting files from a bru
|
||
* archive. It is called once all the command line options
|
||
* have been processed and common initialization has been
|
||
* performed.
|
||
*
|
||
*/
|
||
|
||
VOID extract ()
|
||
{
|
||
DBUG_ENTER ("extract");
|
||
mode = 'x';
|
||
reload ("extraction");
|
||
ar_open ();
|
||
scan (xfile);
|
||
ar_close ();
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* NAME
|
||
*
|
||
* xfile control extraction of a single file
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID xfile (blkp, fip)
|
||
* register union blk *blkp;
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Controls extraction of a single file from the archive.
|
||
* Blkp points to the file header block and fip points
|
||
* to a file information structure.
|
||
*
|
||
* There was initially some question about how to report
|
||
* files which are not extracted because there is an existing
|
||
* file which is more recent. Since files that ARE extracted
|
||
* are only reported if verbosity is enabled, by analogy, files
|
||
* that are NOT extracted are reported only if verbosity is
|
||
* enabled. The basic philosophy is to silently bring the existing
|
||
* hierarchy up to date by replacing missing files and
|
||
* superceding out of date files, without overwriting more
|
||
* recent files. If every file in a given class (regular,
|
||
* directory, block special, etc) is to be extracted regardless
|
||
* of modification date, bru must be explicitly told this via
|
||
* the -u (unconditional) flag.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin xfile
|
||
* Determine whether or not file is a directory
|
||
* If this file is to be extracted then
|
||
* If unconditional extraction or file out of date then
|
||
* If extraction confirmed then
|
||
* Issue verbosity message
|
||
* Do the extraction
|
||
* End if
|
||
* End if
|
||
* Else
|
||
* If file is not a directory and verbosity enabled
|
||
* Warn user that it was not extracted
|
||
* End if
|
||
* End if
|
||
* End if
|
||
* End xfile
|
||
*
|
||
*/
|
||
|
||
static VOID xfile (blkp, fip)
|
||
register union blk *blkp;
|
||
register struct finfo *fip;
|
||
{
|
||
register BOOLEAN is_directory;
|
||
struct stat64 alpha;
|
||
char tmp[256];
|
||
char *savelinkname;
|
||
|
||
DBUG_ENTER ("xfile");
|
||
is_directory = IS_DIR (fip -> statp -> st_mode);
|
||
if ((IS_STEM (fip) && is_directory) || IS_LEAF (fip) || IS_EXTENSION (fip)) {
|
||
/* change absolute path to relative path if -j flag specified */
|
||
if( (flags.jflag) && (*fip->fname == '/') ) {
|
||
if(strcmp(fip->fname, "/") == 0)
|
||
DBUG_VOID_RETURN;
|
||
strcpy(tmp, fip->fname);
|
||
strcpy(fip->fname, &tmp[1]);
|
||
|
||
}
|
||
if(flags.mflag) {
|
||
if(saved_st_dev != fip->statp->st_dev) {
|
||
if(access(fip->fname, 0)) {
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
else {
|
||
stat64(fip->fname, &alpha);
|
||
if(alpha.st_dev == fip->statp->st_dev)
|
||
saved_st_dev = fip->statp->st_dev;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* symbolic links that are hard linked to other symbolic links we
|
||
* have already seen are treated as just links. SGI seems to
|
||
* be one of the few vendors that even allows this; the only way
|
||
* make this work (and remain compatible with older versions of
|
||
* bru both directions) on list and extract is to build the link
|
||
* table, same as on create and estimate... */
|
||
savelinkname = fip->lname;
|
||
if(!IS_FLNK(fip->statp->st_mode) || (fip->statp->st_nlink>1 &&
|
||
getlinkname(fip))) {
|
||
fip->lname = NULL;
|
||
}
|
||
if(!is_directory && fip->statp -> st_nlink > 1) {
|
||
fip->lname = add_link(fip);
|
||
if(!fip->lname && IS_FLNK(fip->statp->st_mode))
|
||
fip->lname = savelinkname;
|
||
}
|
||
|
||
if (unconditional (fip) || out_of_date (fip)) {
|
||
if (confirmed ("x %s", fip)) {
|
||
verbosity (fip);
|
||
do_extract (blkp, fip);
|
||
}
|
||
} else {
|
||
if (!is_directory && flags.vflag > 1) {
|
||
bru_error (ERR_SUPERSEDE, fip -> fname);
|
||
}
|
||
}
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* makelink link file being extracted to an existing file
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID makelink (blkp, fip)
|
||
* union blk *blkp;
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Links the file being extracted (file1) to the existing file
|
||
* (file2) that it is supposed to be linked to.
|
||
*
|
||
* Note that this will fail if file2 does not exist.
|
||
* In this case there is nothing else to be done since the file
|
||
* contents for file2 are not stored in the archived file1.
|
||
*
|
||
* Also note that any existing file1 is unlinked. If the
|
||
* new link cannot be made for some reason, the effective
|
||
* result is the deletion of any existing file1. This
|
||
* could be fixed by first linking an existing file1 to some
|
||
* temporary and then either unlinking or relinking it as
|
||
* necessary once the link between the new file1 and file2
|
||
* is established or denied respectively. Another way is
|
||
* to open the existing file1, unlink the existing file1,
|
||
* then either close file1 or copy it back to a new
|
||
* instance of file1 as necessary.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin makelink
|
||
* If file to be linked to does not exist then
|
||
* Tell user we can't make the link
|
||
* Else
|
||
* Unlink any existing file to be linked
|
||
* If the link to the target file is successful then
|
||
* Set the attributes of the link just made
|
||
* End if
|
||
* End if
|
||
* End makelink
|
||
*
|
||
*/
|
||
|
||
static VOID makelink (blkp, fip)
|
||
union blk *blkp;
|
||
register struct finfo *fip;
|
||
{
|
||
char tmp[256];
|
||
|
||
DBUG_ENTER ("makelink");
|
||
if(flags.jflag) {
|
||
if(!fip->lname) {
|
||
bru_error (ERR_MKLINK_J, fip->fname);
|
||
return;
|
||
}
|
||
if(strcmp(fip->lname, "") && *fip->lname == '/') {
|
||
strcpy(tmp, fip->lname);
|
||
strcpy(fip->lname, &tmp[1]);
|
||
}
|
||
if (!file_access (fip->lname, A_EXISTS, FALSE)) {
|
||
bru_error (ERR_MKLINK, fip->fname, fip->lname);
|
||
} else {
|
||
(VOID) s_unlink (fip->fname);
|
||
if (mklink (fip->lname, fip->fname)) {
|
||
attributes (fip);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
if (!file_access (blkp -> FH.f_lname, A_EXISTS, FALSE)) {
|
||
bru_error (ERR_MKLINK, blkp -> HD.h_name, blkp -> FH.f_lname);
|
||
} else {
|
||
(VOID) s_unlink (blkp -> HD.h_name);
|
||
if (mklink (blkp -> FH.f_lname, blkp -> HD.h_name)) {
|
||
attributes (fip);
|
||
}
|
||
}
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* makefile extract a regular file
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID makefile (blkp, fip)
|
||
* register union blk *blkp;
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Controls extract of a regular file by testing for
|
||
* for it's existance and/or writability. The file
|
||
* will only be extracted if it does not currently exist
|
||
* or if the user has write permission to overwrite an
|
||
* existing version.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUOD CODE
|
||
*
|
||
* Begin makefile
|
||
* If file does not exist then
|
||
* Go ahead and extract it from archive
|
||
* Else
|
||
* If file is not writable then
|
||
* Tell user he can't overwrite the file
|
||
* Else
|
||
* Go ahead and extract it from archive
|
||
* End if
|
||
* End if
|
||
* End makefile
|
||
*
|
||
*/
|
||
|
||
static VOID makefile (blkp, fip)
|
||
register union blk *blkp;
|
||
register struct finfo *fip;
|
||
{
|
||
DBUG_ENTER ("makefile");
|
||
if (!file_access (fip -> fname, A_EXISTS, FALSE)) {
|
||
xregfile (blkp, fip);
|
||
} else {
|
||
if (!file_access (fip -> fname, A_WRITE, FALSE)) {
|
||
bru_error (ERR_OVRWRT, fip -> fname);
|
||
} else {
|
||
xregfile (blkp, fip);
|
||
}
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* xregfile extract a normal file from archive
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID xregfile (blkp, fip)
|
||
* register union blk *blkp;
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* This routine is responsible for extracting a normal file from
|
||
* an archive.
|
||
*
|
||
* Returns TRUE if file was created, FALSE otherwise.
|
||
* Note that return of TRUE does NOT indicate successful
|
||
* extraction, only that a file was created.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin xregfile
|
||
* Initialize the file truncated flag to FALSE
|
||
* Create the file being extracted
|
||
* If couldn't be created then
|
||
* Inform user
|
||
* Else
|
||
* Get number of bytes to extract
|
||
* If any bytes to extract then
|
||
* For each block to be extracted
|
||
* Seek to archive block
|
||
* Read the archive block
|
||
* If extracting a partial block then
|
||
* Only use that many bytes
|
||
* End if
|
||
* Write the bytes to the new file
|
||
* If write did not write requested number then
|
||
* Remember that the file was truncated
|
||
* End if
|
||
* End for
|
||
* If last write returned error then
|
||
* Notify user about write error
|
||
* End if
|
||
* If file was truncated then
|
||
* Warn user about truncation
|
||
* End if
|
||
* End if
|
||
* If file close get an error then
|
||
* Warn user about the close error
|
||
* End if
|
||
* Set the attributes of the extracted file
|
||
* End if
|
||
* End xregfile
|
||
*
|
||
*/
|
||
|
||
static VOID xregfile (blkp, fip)
|
||
register union blk *blkp;
|
||
register struct finfo *fip;
|
||
{
|
||
register off_t bytes;
|
||
register int iobytes;
|
||
register UINT wbytes;
|
||
register int fildes;
|
||
register BOOLEAN truncated = FALSE;
|
||
register BOOLEAN doextract = TRUE;
|
||
register char *fname;
|
||
|
||
DBUG_ENTER ("xregfile");
|
||
if (IS_COMPRESSED (fip)) {
|
||
if (openzfile (fip)) {
|
||
fname = fip -> zfname;
|
||
fildes = fip -> zfildes;
|
||
} else {
|
||
bru_error (ERR_ZXFAIL, fip -> fname);
|
||
doextract = FALSE;
|
||
}
|
||
} else {
|
||
fname = fip -> fname;
|
||
fildes = s_creat (fip -> fname, 0600);
|
||
if (fildes == SYS_ERROR) {
|
||
bru_error (ERR_CREAT, fip -> fname);
|
||
doextract = FALSE;
|
||
}
|
||
}
|
||
if (doextract) {
|
||
bytes = ZSIZE (fip);
|
||
if (bytes > 0) {
|
||
for (wbytes = DATASIZE; bytes > 0; bytes -= wbytes) {
|
||
blkp = ar_next ();
|
||
ar_read (fip);
|
||
if (bytes < DATASIZE) {
|
||
wbytes = bytes;
|
||
}
|
||
iobytes = s_write (fildes, blkp -> FD, wbytes);
|
||
if (iobytes != wbytes) {
|
||
truncated = TRUE;
|
||
}
|
||
}
|
||
if (iobytes == SYS_ERROR) {
|
||
bru_error (ERR_WRITE, fname);
|
||
}
|
||
if (truncated) {
|
||
bru_error (ERR_FTRUNC, fname);
|
||
}
|
||
}
|
||
if (s_close (fildes) == SYS_ERROR) {
|
||
bru_error (ERR_CLOSE, fname);
|
||
}
|
||
if (!IS_COMPRESSED (fip) || decompfip (fip)) {
|
||
attributes (fip);
|
||
}
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* makespecial make a special file node
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID makespecial (fip)
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Is reponsible for controlling creation of special files.
|
||
* If the special file being made already exists, it is
|
||
* unlinked first, then the new node is made.
|
||
*
|
||
* Adding code for 4.2 has complicated this routine, and made it
|
||
* very ugly. It is even worse than it ought to be, since it is
|
||
* currently impossible under 4.2 to chmod a symbolic link (although
|
||
* a symlink with mode 000 can still be read!), or to reset the access
|
||
* and modification times on a symbolic link. This is dealt with
|
||
* in attributes(), below.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin makespecial
|
||
* If the current file exists then
|
||
* Unlink the file
|
||
* End if
|
||
* If under 4.2, not a pyramid, and file is a fifo, then
|
||
* Just create a regular file
|
||
* Set the attributes of the new node
|
||
* Else if the file is a symbolic link then
|
||
* If cannot make the symbolic link then
|
||
* Inform the user
|
||
* Else
|
||
* Set the attributes of the new node
|
||
* Endif
|
||
* Else if the new node cannot be made then
|
||
* Inform the user
|
||
* Else
|
||
* Set attributes of the new node
|
||
* End if
|
||
* End makespecial
|
||
*
|
||
*/
|
||
|
||
static VOID makespecial (fip)
|
||
register struct finfo *fip;
|
||
{
|
||
char *tmpname;
|
||
DBUG_ENTER ("makespecial");
|
||
if (file_access (fip -> fname, A_EXISTS, FALSE)) {
|
||
(VOID) s_unlink (fip -> fname);
|
||
}
|
||
#if !HAVE_FIFOS
|
||
if (IS_FIFO (fip -> statp -> st_mode)) {
|
||
int fd;
|
||
|
||
fd = s_creat (fip -> fname, 0666); /* fudge it */
|
||
if (fd == SYS_ERROR) {
|
||
bru_error (ERR_MKFIFO, fip -> fname);
|
||
} else {
|
||
attributes (fip);
|
||
bru_error (ERR_FIFOTOREG, fip -> fname);
|
||
if (s_close (fd) == SYS_ERROR) {
|
||
bru_error (ERR_CLOSE, fip -> fname);
|
||
}
|
||
}
|
||
} else
|
||
#endif
|
||
if (IS_FLNK (fip -> statp -> st_mode)) {
|
||
if (! mksymlink (fip -> lname, fip -> fname) && ! flags.lflag) {
|
||
bru_error (ERR_MKSYMLINK, fip -> fname);
|
||
} else {
|
||
attributes (fip);
|
||
}
|
||
} else if (s_mknod (fip -> fname, (int) fip -> statp -> st_mode, (int) fip -> statp -> st_rdev) == SYS_ERROR) {
|
||
bru_error (ERR_MKNOD, fip -> fname);
|
||
} else {
|
||
attributes (fip);
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* attributes set attributes of newly created file
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID attributes (fip)
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Using stat buffer information recovered from archive
|
||
* header block, sets the attributes of the file.
|
||
*
|
||
* Under 4.2 BSD, it is impossible to reset the access and
|
||
* modification times of a symbolic link, or to change its
|
||
* mode with chmod. Both chmod and utimes go through the
|
||
* symbolic link to the real file, which is not what we want.
|
||
* Therefore, we add code for checking symbolic links.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin attributes
|
||
* If the mode cannot be set then
|
||
* Inform user that mode cannot be changed
|
||
* End if
|
||
* Change owner and group of the file
|
||
* If the access and modification times cannot be set then
|
||
* Inform the user about time error
|
||
* End if
|
||
* End attributes
|
||
*
|
||
*/
|
||
|
||
static VOID attributes (fip)
|
||
register struct finfo *fip;
|
||
{
|
||
register char *path;
|
||
register int s_mode;
|
||
register int s_owner;
|
||
register int s_group;
|
||
struct filetimes { /* 4.2 changed the stat buffer; st_atime */
|
||
time_t atime; /* is not contiguous to st_mtime, so we */
|
||
time_t mtime; /* build the structure explicitly before */
|
||
} t; /* handing it off to s_utime() */
|
||
|
||
DBUG_ENTER ("attributes");
|
||
path = fip -> fname;
|
||
s_mode = fip -> statp -> st_mode;
|
||
s_owner = fip -> statp -> st_uid;
|
||
s_group = fip -> statp -> st_gid;
|
||
if (s_owner != info.bru_uid && (info.bru_uid != 0 || flags.Cflag)) {
|
||
if (s_mode & S_ISUID) {
|
||
s_mode &= ~S_ISUID;
|
||
bru_error (ERR_SUID, path);
|
||
}
|
||
}
|
||
if (s_group != info.bru_gid && (info.bru_uid != 0 || flags.Cflag)) {
|
||
if (s_mode & S_ISGID) {
|
||
s_mode &= ~S_ISGID;
|
||
bru_error (ERR_SGID, path);
|
||
}
|
||
}
|
||
|
||
DBUG_PRINT ("attributes",
|
||
("file %s, owner %d, group %d", fip -> fname, s_owner, s_group));
|
||
DBUG_PRINT ("attributes", ("file %s, mode %#o", fip -> fname, s_mode));
|
||
|
||
if(!IS_FLNK (fip -> statp -> st_mode)) {
|
||
t.atime = fip -> statp -> st_atime;
|
||
t.mtime = fip -> statp -> st_mtime;
|
||
/* do in this order so when on sysV systems, the time
|
||
* and mode can be set correctly even if we are chown'ing
|
||
* the file to some other user. */
|
||
if(s_utime (fip -> fname, &t) == SYS_ERROR)
|
||
bru_error (ERR_STIMES, fip -> fname);
|
||
if(s_chmod (path, s_mode) == SYS_ERROR)
|
||
bru_error (ERR_SMODE, path);
|
||
file_chown (path, s_owner, s_group);
|
||
}
|
||
if (fip -> fi_flags & FI_AMIGA) {
|
||
(VOID) setinfo (fip);
|
||
if (flags.Asflag) {
|
||
mark_archived (fip);
|
||
} else if (flags.Acflag) {
|
||
clear_archived (fip);
|
||
}
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* do_extract do the extraction
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
*
|
||
* static VOID do_extract (blkp, fip)
|
||
* register union blk *blkp;
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Once all tests have been met and it has been decided to extract
|
||
* the file, this routine is called to do the actual extraction.
|
||
* At this point, we are committed to doing the extraction and
|
||
* cannot be interrupted until done.
|
||
*
|
||
* Any missing parent directories will be created and then the
|
||
* file itself will be extracted.
|
||
*
|
||
* For directories, there is an explicit call to set the attributes
|
||
* since the makedir function cannot do this if the directory already
|
||
* exists. Thus directories which do not exist will have there
|
||
* attributes set when they are made by makedir, and again here.
|
||
* It is the case of existing directories that we only catch here.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin do_extract
|
||
* Save current signal processing stat
|
||
* Set up to catch signals
|
||
* Check for directory in which to extract file
|
||
* If no parent directory then
|
||
* Make a default stat buffer
|
||
* Make the parent directory
|
||
* End if
|
||
* If parent directory and parent accessible then
|
||
* If file to extract is a directory then
|
||
* Make the directory
|
||
* Set the directory attributes
|
||
* Else if file linked to another then
|
||
* Make the linkage
|
||
* Else if not a regular file then
|
||
* Make a special file
|
||
* Else
|
||
* Make the file
|
||
* End if
|
||
* End if
|
||
* Pop the saved signal processing state
|
||
* If interrupt came in while doing extraction
|
||
* Cleanup and exit
|
||
* End if
|
||
* End do_extract
|
||
*
|
||
*/
|
||
|
||
|
||
static VOID do_extract (blkp, fip)
|
||
register union blk *blkp;
|
||
register struct finfo *fip;
|
||
{
|
||
auto struct stat64 sbuf;
|
||
register BOOLEAN got_parent;
|
||
char *tmpname;
|
||
SIGTYPE prevINT;
|
||
SIGTYPE prevQUIT;
|
||
|
||
DBUG_ENTER ("do_extract");
|
||
sig_push (&prevINT, &prevQUIT);
|
||
sig_catch ();
|
||
got_parent = dir_access (fip -> fname, A_EXISTS, FALSE);
|
||
if (!got_parent) {
|
||
makestat (&sbuf);
|
||
got_parent = makeparent (&sbuf, fip -> fname);
|
||
}
|
||
if (got_parent) {
|
||
if (IS_DIR (fip -> statp -> st_mode)) {
|
||
(VOID) makedir (fip);
|
||
attributes (fip);
|
||
} else if (LINKED (blkp) && (!IS_FLNK (fip -> statp -> st_mode)) ||
|
||
fip->statp->st_nlink>1 && (tmpname=getlinkname(fip)) &&
|
||
strcmp(fip->fname, tmpname)) {
|
||
/* make hard link if not a symlink, or a symlink with st_nlink>1
|
||
* and this is the 2nd thru nth instance seen of the hardlink
|
||
* to the symlink during the backup. */
|
||
makelink (blkp, fip);
|
||
} else if (!IS_REG (fip -> statp -> st_mode)) {
|
||
/*
|
||
* blkp and fip are passed down from scan, which will already
|
||
* set up the link name in fip, so makespecial can just pass
|
||
* it on if a symbolic link has to be made. In other words,
|
||
* we don't need to do any special checking for that here.
|
||
*/
|
||
makespecial (fip);
|
||
} else {
|
||
makefile (blkp, fip);
|
||
}
|
||
}
|
||
sig_pop (&prevINT, &prevQUIT);
|
||
if (interrupt) {
|
||
done ();
|
||
}
|
||
DBUG_VOID_RETURN;
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* makeparent make a parent directory that does not exist
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static BOOLEAN makeparent (statp, name)
|
||
* struct stat64 *statp;
|
||
* char *name;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Given pointer to a directory pathname in "name" and
|
||
* pointer to stat buffer in "statp", attempts to make
|
||
* the parent directory if one does not already exist.
|
||
*
|
||
* Note that the installation is recursive. That is,
|
||
* if the parent of the parent does not exist, it is
|
||
* made also, with the same attributes.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin makeparent
|
||
* If parent already exists then
|
||
* Result is TRUE
|
||
* Else
|
||
* Find where parent and child names join.
|
||
* If no parent name
|
||
* Tell user about bug
|
||
* Result is FALSE
|
||
* Else
|
||
* Split parent and child names
|
||
* Attempt to make grandparent recursively
|
||
* If attempt successful then
|
||
* Build a file info struct for parent
|
||
* Make the parent
|
||
* End if
|
||
* Rejoin parent and child names
|
||
* End if
|
||
* End if
|
||
* Return result
|
||
* End makeparent
|
||
*
|
||
*/
|
||
|
||
static BOOLEAN makeparent (statp, name)
|
||
struct stat64 *statp;
|
||
char *name;
|
||
{
|
||
register char *slash;
|
||
register BOOLEAN got_dir;
|
||
auto struct finfo pfile;
|
||
|
||
DBUG_ENTER ("makeparent");
|
||
if (dir_access (name, A_EXISTS, FALSE)) {
|
||
got_dir = TRUE;
|
||
} else {
|
||
slash = s_strrchr (name, '/');
|
||
if (slash == NULL) {
|
||
bru_error (ERR_BUG, "makeparent");
|
||
got_dir = FALSE;
|
||
} else {
|
||
if(slash != name) {
|
||
*slash = EOS;
|
||
if(got_dir = makeparent (statp, name)) {
|
||
finfo_init (&pfile, name, statp);
|
||
got_dir = makedir (&pfile);
|
||
}
|
||
*slash = '/';
|
||
}
|
||
else /* makedir called directly later if it's a dir; if
|
||
it isn't, we definitely do not want to mkdir! */
|
||
got_dir = 1; /* but makeparent still has to return true */
|
||
}
|
||
}
|
||
DBUG_RETURN (got_dir);
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* makedir make a directory
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static BOOLEAN makedir (fip)
|
||
* register struct finfo *fip;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Attempts to make a directory if one does not already exist.
|
||
*
|
||
* Note that it is assumed that the parent has already
|
||
* been made by a call to makeparent. If not, then
|
||
* makedir will fail if no parent exists. Also, we need to
|
||
* verify that the user running bru has appropriate permission
|
||
* to write into the parent of the directory to be made, rather
|
||
* than depending upon the newdir/mkdir calls to enforce
|
||
* permissions.
|
||
*
|
||
* Also note that the attributes are set ONLY if the directory
|
||
* is made. This is because makedir may be called to ensure
|
||
* that parent directories exist in order to extract an archived
|
||
* directory. If the attributes were unconditionally set,
|
||
* parents would take on the attributes of the child directory
|
||
* being extracted from the archive. The actual directory
|
||
* being extracted has it attributes explicitly set elsewhere.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin makedir
|
||
* If the directory already exists then
|
||
* Result is TRUE
|
||
* Else if we can write in parent to make directory then
|
||
* Attempt to make the directory
|
||
* If directory node successfully made then
|
||
* Set attributes of directory
|
||
* End if
|
||
* Else
|
||
* Doesn't exist and can't make it, result is FALSE
|
||
* End if
|
||
* Return result
|
||
* End makedir
|
||
*
|
||
*/
|
||
|
||
static BOOLEAN makedir (fip)
|
||
register struct finfo *fip;
|
||
{
|
||
register BOOLEAN result;
|
||
|
||
DBUG_ENTER ("makedir");
|
||
if (file_access (fip -> fname, A_EXISTS, FALSE)) {
|
||
result = TRUE;
|
||
} else {
|
||
/* idiotic program; don't check permissions, just
|
||
* try to make the directory. */
|
||
result = newdir (fip);
|
||
if (result) {
|
||
attributes (fip);
|
||
}
|
||
}
|
||
DBUG_RETURN (result);
|
||
}
|
||
|
||
|
||
/*
|
||
* FUNCTION
|
||
*
|
||
* makestat make a default stat buffer
|
||
*
|
||
* SYNOPSIS
|
||
*
|
||
* static VOID makestat (statp)
|
||
* struct stat64 *statp;
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* Makes a default stat buffer as appropriate for current user.
|
||
* This is typically used to set attributes of directories which
|
||
* must be made to install a file being extracted.
|
||
*
|
||
*/
|
||
|
||
|
||
/*
|
||
* PSEUDO CODE
|
||
*
|
||
* Begin makestat
|
||
* Get current time
|
||
* Get the mode word mask
|
||
* Restore the mode word mask
|
||
* Get the file mode and make it a directory
|
||
* Initialize the stat buffer mode
|
||
* Initialize the user id
|
||
* Initialize the group id
|
||
* Initialize the access time
|
||
* Initialize the modification time
|
||
* End makestat
|
||
*
|
||
*/
|
||
|
||
static VOID makestat (statp)
|
||
struct stat64 *statp;
|
||
{
|
||
register int mask;
|
||
register time_t now;
|
||
|
||
DBUG_ENTER ("makestat");
|
||
now = (time_t) s_time ((long *) 0);
|
||
mask = s_umask (0);
|
||
(VOID) s_umask (mask);
|
||
statp -> st_mode = (ushort) (S_IFDIR | (DIR_MAGIC & ~mask));
|
||
statp -> st_uid = info.bru_uid;
|
||
statp -> st_gid = info.bru_gid;
|
||
statp -> st_atime = now;
|
||
statp -> st_mtime = now;
|
||
DBUG_VOID_RETURN;
|
||
}
|