|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T e
Length: 7540 (0x1d74)
Types: TextFile
Names: »extract.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec1/pdtar/extract.c«
/*
* Extract files from a tar archive.
*
* Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#) extract.c 1.17 86/10/29 Public Domain - gnu
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef BSD42
#include <sys/file.h>
#endif
#ifdef USG
#include <fcntl.h>
#endif
extern int errno; /* From libc.a */
extern char *index(); /* From libc.a or port.c */
#include "tar.h"
extern union record *head; /* Points to current tape header */
extern struct stat hstat[1]; /* Stat struct corresponding */
extern void print_header();
extern void skip_file();
extern void pr_mkdir();
int make_dirs(); /* Makes required directories */
time_t now = 0; /* Current time */
/*
* Extract a file from the archive.
*/
void
extract_archive()
{
register char *data;
int fd, check, namelen, written;
long size;
time_t acc_upd_times[2];
int standard; /* Is header standard? */
saverec(&head); /* Make sure it sticks around */
userec(head); /* And go past it in the archive */
decode_header(head, hstat, &standard, 1); /* Snarf fields */
/* Print the record from 'head' and 'hstat' */
if (f_verbose)
print_header();
switch (head->header.linkflag) {
default:
annofile(stderr, tar);
fprintf(stderr, "Unknown file type %d for %s\n",
head->header.linkflag, head->header.name);
/* FALL THRU */
case LF_OLDNORMAL:
case LF_NORMAL:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen(head->header.name)-1;
if (head->header.name[namelen] == '/')
goto really_dir;
/* FIXME, deal with protection issues */
/* FIXME, f_keep doesn't work on V7, st_mode loses too */
again_file:
fd = open(head->header.name,
f_keep?
O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL:
O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
hstat->st_mode);
if (fd < 0) {
if (make_dirs(head->header.name))
goto again_file;
annofile(stderr, tar);
fprintf(stderr, "Could not make file ");
perror(head->header.name);
skip_file((long)hstat->st_size);
goto quit;
}
for (size = hstat->st_size;
size > 0;
size -= written) {
/*
* Locate data, determine max length
* writeable, write it, record that
* we have used the data, then check
* if the write worked.
*/
data = findrec()->charptr;
written = endofrecs()->charptr - data;
if (written > size) written = size;
errno = 0;
check = write (fd, data, written);
/*
* The following is in violation of strict
* typing, since the arg to userec
* should be a struct rec *. FIXME.
*/
userec(data + written - 1);
if (check == written) continue;
/*
* Error in writing to file.
* Print it, skip to next file in archive.
*/
annofile(stderr, tar);
fprintf(stderr,
"Tried to write %d bytes to file, could only write %d:\n",
written, check);
perror(head->header.name);
(void) close(fd);
skip_file((long)(size - written));
goto quit;
}
check = close(fd);
if (check < 0) {
annofile(stderr, tar);
fprintf(stderr, "Error while closing ");
perror(head->header.name);
}
/* FIXME, deal with uid/gid/mtimes/suid */
/*
* Set the modified time of the file.
*
* Note that we set the accessed time to "now", which
* is really "the time we started extracting files".
*/
if (!f_modified) {
if (!now)
now = time((time_t *)0); /* Just do it once */
acc_upd_times[0] = now; /* Accessed now */
acc_upd_times[1] = hstat->st_mtime; /* Mod'd */
if (utime(head->header.name, acc_upd_times) < 0) {
annofile(stderr, tar);
perror(head->header.name);
}
}
/*
* If '-p' is not set, OR if the file has pretty normal
* mode bits, we can skip the chmod and save a sys call.
* This works because we did umask(0) if -p is set, so
* the open() that created the file will have set the modes
* properly.
* FIXME: I don't know what open() does w/UID/GID/SVTX bits.
* However, if we've done a chown(), they got reset.
*/
if (f_use_protection
&& (hstat->st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
if (chmod(head->header.name, (int)hstat->st_mode) < 0) {
annofile(stderr, tar);
perror(head->header.name);
}
}
quit:
break;
case LF_LINK:
again_link:
check = link (head->header.linkname,
head->header.name);
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
if (make_dirs(head->header.linkname))
goto again_link;
annofile(stderr, tar);
fprintf(stderr, "Could not link %s to ",
head->header.name);
perror(head->header.linkname);
break;
#ifdef S_IFLNK
case LF_SYMLINK:
again_symlink:
check = symlink(head->header.linkname,
head->header.name);
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
if (make_dirs(head->header.linkname))
goto again_symlink;
annofile(stderr, tar);
fprintf(stderr, "Could not create symlink ");
perror(head->header.linkname);
break;
#endif
case LF_CHR:
hstat->st_mode |= S_IFCHR;
goto make_node;
case LF_BLK:
hstat->st_mode |= S_IFBLK;
make_node:
check = mknod(head->header.name, (int) hstat->st_mode,
(int) hstat->st_dev);
if (check != 0) {
if (make_dirs(head->header.name))
goto make_node;
annofile(stderr, tar);
fprintf(stderr, "Could not make special file ");
perror(head->header.name);
break;
};
break;
case LF_DIR:
/* Check for trailing / */
namelen = strlen(head->header.name)-1;
really_dir:
while (namelen && head->header.name[namelen] == '/')
head->header.name[namelen--] = '\0'; /* Zap / */
/* FIXME, deal with umask */
again_dir:
check = mkdir(head->header.name, (int)hstat->st_mode);
if (check != 0) {
if (make_dirs(head->header.name))
goto again_dir;
annofile(stderr, tar);
fprintf(stderr, "Could not make directory ");
perror(head->header.name);
break;
}
/* FIXME, deal with uid/gid */
/* FIXME, Remember timestamps for after files created? */
break;
case LF_FIFO:
abort(); /* FIXME */
break;
}
/* We don't need to save it any longer. */
saverec((union record **) 0); /* Unsave it */
}
/*
* After a file/link/symlink/dir creation has failed, see if
* it's because some required directory was not present, and if
* so, create all required dirs.
*/
int
make_dirs(pathname)
char *pathname;
{
char *p; /* Points into path */
int madeone = 0; /* Did we do anything yet? */
int save_errno = errno; /* Remember caller's errno */
int check;
if (errno != ENOENT)
return 0; /* Not our problem */
for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
/* Avoid mkdir of empty string, if leading or double '/' */
if (p == pathname || p[-1] == '/')
continue;
/* Avoid mkdir where last part of path is '.' */
if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
continue;
*p = 0; /* Truncate the path there */
check = mkdir (pathname, 0777); /* Try to create it as a dir */
*p = '/';
if (check == 0) {
/* FIXME chown, chgrp it same as file being created */
/* FIXME, show mode as modified by current umask */
pr_mkdir(pathname, p-pathname, 0777);
madeone++; /* Remember if we made one */
continue;
}
if (errno == EEXIST) /* Directory already exists */
continue;
/*
* Some other error in the mkdir. We return to the caller.
*/
break;
}
errno = save_errno; /* Restore caller's errno */
return madeone; /* Tell them to retry if we made one */
}