/* compile cc -o tar -wa tar.c chmem =65536 tar */ #define _MINIX #define _POSIX_SOURCE #define MINIX /* tar - tape archiver Author: Michiel Huisjes */ /* Usage: tar [-][cxt][svoB][F][f] [size] [Block size] tapefile [files] * * attempt to make tar to conform to POSIX 1003.1 * disclaimer: based on an old (1986) POSIX draft. * Klamer Schutte, 20/9/89 * * Changes: * Changed to handle the original minix-tar format. KS 22/9/89 * Changed to handle BSD4.3 tar format. KS 22/9/89 * Conform to current umask if not super-user. KS 22/9/89 * Update usage message to show f option KS 22/9/89 * * change to support disk size with [s] parameter CTA 12/4/94 * change to support block size [B] to get best disk performance * CTA 22/1/95 * * * 1) tar will back itself up, should check archive inode num(&dev) and then check the target inode number. In verbose mode, issue warning, in all cases ignore target. marks@mgse Mon Sep 25 10:38:58 CDT 1989 added global varaibles, made changes to main() and add_file(); maks@mgse Mon Sep 25 12:09:20 CDT 1989 2) tar will not notice that a file has changed size while it was being backed up. should issue warning. marks@mgse Mon Sep 25 10:38:58 CDT 1989 3) the 'f' option was not documented in usage[]. marks@mgse Mon Sep 25 12:03:20 CDT 1989 changed both usage[] defines. Why are there two (one is commented out)? ( deleted by me (was done twice) -- KS, 2/10/89 ) * * changed stat on tar_fd to an fstat KS 2/10/89 * deleted mkfifo() code -- belongs in libc.a KS 2/10/89 * made ar_dev default to -1 : an illegal device KS 2/10/89 * made impossible to chown if normal user KS 2/10/89 * if names in owner fields not known use numirical values KS 2/10/89 * creat with mask 666 -- use umask if to liberal KS 2/10/89 * allow to make directories as ../directory KS 2/10/89 * allow tmagic field to end with a space (instead of \0) KS 2/10/89 * correct usage of tmagic field KS 3/10/89 * made mkdir() to return a value if directory == "." KS 3/10/89 * made lint complains less (On a BSD 4.3 system) KS 3/10/89 * use of directory(3) routines KS 3/10/89 * deleted use of d_namlen selector of struct dirent KS 18/10/89 * forget inodes when link count expires EC 6/4/91 * don't remember directories *twice*! * * Bugs: * verbose mode is not reporting consistent * code needs cleanup * prefix field is not used * timestamp of a directory will not be correct if there are files to be * unpacked in the directory * (add you favorite bug here (or two (or three (or ...)))) */ #include #include #include #include #include #include #include /* need NULL */ #include #define POSIX_COMP /* POSIX compatible */ #define DIRECT_3 /* use directory(3) routines */ #define DEFAULT 512 /* default output block size */ #ifdef DIRECT_3 #ifndef BSD /* To all minix users: i am sorry, developed this piece of code on a * BSD system. KS 18/10/89 */ #include #define direct dirent /* stupid BSD non-POSIX compatible name! */ #else /* BSD */ #include #include #endif /* BSD */ #endif /* DIRECT_3 */ #ifdef S_IFIFO #define HAVE_FIFO /* have incorporated Simon Pooles' changes */ #endif typedef char BOOL; #define TRUE 1 #define FALSE 0 #define HEADER_SIZE TBLOCK #define NAME_SIZE NAMSIZ /* #define BLOCK_BOUNDARY 20 -- not in POSIX ! */ typedef union hblock HEADER; /* Make the MINIX member names overlap to the POSIX names */ #define m_name name #define m_mode mode #define m_uid uid #define m_gid gid #define m_size size #define m_time mtime #define m_checksum chksum #define m_linked typeflag #define m_link linkname #define hdr_block dummy #define m header #define member dbuf #if 0 /* original structure -- see tar.h for new * structure */ typedef union { char hdr_block[HEADER_SIZE]; struct m { char m_name[NAME_SIZE]; char m_mode[8]; char m_uid[8]; char m_gid[8]; char m_size[12]; char m_time[12]; char m_checksum[8]; char m_linked; char m_link[NAME_SIZE]; } member; } HEADER; #endif /* Structure used to note links */ struct link { ino_t ino; dev_t dev; nlink_t nlink; struct link *next; char name[1]; } *link_top = NULL; HEADER header; #define INT_TYPE (sizeof(header.member.m_uid)) #define LONG_TYPE (sizeof(header.member.m_size)) #define MKDIR "/bin/mkdir" #define NIL_HEADER ((HEADER *) 0) #define NIL_PTR ((char *) 0) #define BLOCK_SIZE TBLOCK #define flush() print(NIL_PTR) BOOL show_fl, creat_fl, ext_fl; int tar_fd; char usage[] = "Usage: tar [-][cxt][svo][B][F][f] [size] [block size] tarfile [files]."; char io_buffer[BLOCK_SIZE]; char path[NAME_SIZE]; char pathname[NAME_SIZE]; int force_flag = 0; #ifdef ORIGINAL_DEFAULTS int chown_flag = 1; int verbose_flag = 1; #else int chown_flag = 0; int verbose_flag = 0; #endif /* variables to handle disk size */ char tar_file[NAME_SIZE]; int total_blocks; /* total disk block */ int maxlbn; /* maximun lbn into disk */ int size; /* size of disk */ int dsknro; /* disk number */ extern char *date(); #define MAX_BLK 20 #define MAX_BUF MAX_BLK*BLOCK_SIZE char obuf[MAX_BUF]; /* output buffer for tar */ int bsize; /* block size */ int blkptr; /* pointer into buffer */ /* Make sure we don't tar ourselves. marks@mgse Mon Sep 25 12:06:28 CDT 1989 */ ino_t ar_inode; /* archive inode number */ dev_t ar_dev; /* archive device number */ int u_mask; /* one's complement of current umask */ long convert(); #define block_size() (int) ((convert(header.member.m_size, LONG_TYPE) \ + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE) error(s1, s2) char *s1, *s2; { string_print(NIL_PTR, "%s %s\n", s1, s2 ? s2 : ""); flush(); exit(1); } BOOL get_header(); main(argc, argv) int argc; register char *argv[]; { register char *mem_name; register char *ptr; struct stat st; int i,arg; arg = 2; /* count without disk size */ size = 0; /* no size check */ dsknro = 1; /* begin with disk 1 */ bsize = 1; /* default transfer size for output */ if (argc < 3) error(usage, NIL_PTR); for (ptr = argv[1]; *ptr; ptr++) { switch (*ptr) { case 'c': creat_fl = TRUE; break; case 'x': ext_fl = TRUE; break; case 't': show_fl = TRUE; break; case 'v': /* verbose output -Dal */ verbose_flag = !verbose_flag; break; case 'o': /* chown/chgrp files -Dal */ chown_flag = TRUE; break; case 'F': /* IGNORE ERRORS -Dal */ force_flag = TRUE; break; case 'f': /* standard U*IX usage -KS */ break; case '-': /* standard switch flag */ break; case 's': size = atoi(argv[arg++]); if (size <= 0) { fprintf(stderr,"Invalid disk size %d.\n", size); size = 0; } maxlbn = 2 * size; break; case 'B': bsize = atoi(argv[arg++]); if ((bsize <= 0)||(bsize > MAX_BLK)) { fprintf(stderr,"Invalid block size %d.\n", size); bsize = 1; } break; default: error(usage, NIL_PTR); } } clear_buffer(); #ifdef DEBUG fprintf(stderr,"tar: block size %d (%d bytes).\n",bsize,bsize*DEFAULT); flush(); #endif if (creat_fl + ext_fl + show_fl != 1) error(usage, NIL_PTR); if (creat_fl == TRUE) blkptr = 0; /* pointer cleared if creating tar */ else blkptr = bsize; strcpy(tar_file, argv[arg]); /* copio filename */ if (strcmp(argv[arg], "-") == 0) { /* only - means stdin/stdout - KS */ maxlbn = 0; size = 0; tar_fd = creat_fl ? 1 : 0; /* '-' means used * stdin/stdout -Dal */ } else tar_fd = creat_fl ? creat(argv[arg], 0666) : open(argv[arg], O_RDONLY); if (tar_fd < 0) { perror("tar_fd"); error("Cannot open (tar_fd < 0)", argv[arg]); } if (geteuid()) { /* check if super-user */ int save_umask; save_umask = umask(0); u_mask = ~save_umask; umask(save_umask); chown_flag = TRUE; /* normal user can't chown */ } else u_mask = 0777; /* don't restrict if 'privileged utiliy' */ ar_dev = -1; /* impossible device nr */ if (creat_fl) { if (tar_fd > 1 && fstat(tar_fd, &st) < 0) { perror("fstat"); error("Can't stat ", argv[arg]); /* will never be here, * right? */ } else { /* get archive inode & device */ ar_inode = st.st_ino; /* save files inode */ ar_dev = st.st_dev; /* save files device */ } /* marks@mgse Mon Sep 25 11:30:45 CDT 1989 */ for (i = (arg+1); i < argc; i++) { add_file(argv[i]); path[0] = '\0'; } adjust_boundary(); } else if (ext_fl) { /* extraction code moved here from tarfile() MSP */ while (get_header()) { mem_name = header.member.m_name; if (is_dir(mem_name)) { for (ptr = mem_name; *ptr; ptr++); *(ptr - 1) = '\0'; header.dbuf.typeflag = '5'; } for (i = (arg+1); i < argc; i++) if (!strncmp(argv[i], mem_name, strlen(argv[i]))) break; if (argc == 3 || (i < argc)) { extract(mem_name); } else if (header.dbuf.typeflag == '0' || header.dbuf.typeflag == 0 || header.dbuf.typeflag == ' ') skip_entry(); flush(); } } else tarfile(); /* tarfile() justs prints info. now MSP */ flush(); sync(); sync(); exit(0); } BOOL get_header() { register int check; #ifdef DEBUG fprintf(stderr,"tar: getting header ...\n"); flush(); #endif mread(tar_fd, (char *) &header, sizeof(header)); if (header.member.m_name[0] == '\0') return FALSE; if (force_flag) /* skip checksum verification -Dal */ return TRUE; check = (int) convert(header.member.m_checksum, INT_TYPE); if (check != checksum()) error("Tar: header checksum error.", NIL_PTR); return TRUE; } /* tarfile() just lists info about archive now; as of the t flag. */ /* Extraction has been moved into main() as that needs access to argv[] */ tarfile() { register char *ptr; register char *mem_name; int f_mode,gr_id,us_id; while (get_header()) { mem_name = header.member.m_name; switch (header.dbuf.typeflag) { case '1': string_print(NIL_PTR, "%s%s", mem_name, (verbose_flag ? " " : "\n")); verb_print("linked to", header.dbuf.linkname); break; case '6': /* string_print(NIL_PTR, "%s%s", mem_name, (verbose_flag ? " " : "\n")); verb_print("", "fifo"); */ if (verbose_flag) { f_mode = convert(header.member.m_mode,INT_TYPE); gr_id = convert(header.member.m_gid, INT_TYPE); us_id = convert(header.member.m_uid, INT_TYPE); printf("%6o %3d %3d ", f_mode, us_id, gr_id); printf("%s fifo ", date(convert(header.member.m_time, LONG_TYPE))); printf(" %s\n", mem_name); } else string_print(NIL_PTR, "%s%s\n", mem_name); break; case '3': case '4': /* string_print(NIL_PTR, "%s%s", mem_name, (verbose_flag ? " " : "\n")); */ if (verbose_flag) { /* string_print(NIL_PTR, "%s special file major %s minor %s\n", (header.dbuf.typeflag == '3' ? "character" : "block"), header.dbuf.devmajor, header.dbuf.devminor); */ f_mode = convert(header.member.m_mode,INT_TYPE); gr_id = convert(header.member.m_gid, INT_TYPE); us_id = convert(header.member.m_uid, INT_TYPE); printf("%6o %3d %3d ", f_mode, us_id, gr_id); printf("%s %3d, %3d ", date(convert(header.member.m_time, LONG_TYPE)), atoi(header.dbuf.devmajor), atoi(header.dbuf.devminor)); printf("%s",(header.dbuf.typeflag == '3' ? "char " : "block")); printf(" %s\n", mem_name); } else string_print(NIL_PTR, "%s%s\n", mem_name); break; case '0': /* official POSIX */ case 0: /* also mentioned in POSIX */ case ' ': /* ofetn used */ if (!is_dir(mem_name)) { if (verbose_flag) { /* string_print(NIL_PTR, "%d tape blocks\n", block_size()); */ f_mode = convert(header.member.m_mode,INT_TYPE); gr_id = convert(header.member.m_gid, INT_TYPE); us_id = convert(header.member.m_uid, INT_TYPE); printf("%6o %3d %3d ", f_mode, us_id, gr_id); printf("%s %8D bytes", date(convert(header.member.m_time, LONG_TYPE)), convert(header.member.m_size,LONG_TYPE), block_size()); printf(" %s\n", mem_name); } else string_print(NIL_PTR, "%s%s\n", mem_name); skip_entry(); break; } else {} /* FALL TROUGH */ case '5': /* string_print(NIL_PTR, "%s%s", mem_name, (verbose_flag ? " " : "\n")); verb_print("", "directory"); */ if (verbose_flag) { f_mode = convert(header.member.m_mode,INT_TYPE); gr_id = convert(header.member.m_gid, INT_TYPE); us_id = convert(header.member.m_uid, INT_TYPE); printf("%6o %3d %3d ", f_mode, us_id, gr_id); printf("%s ", date(convert(header.member.m_time, LONG_TYPE))); printf(" directory"); printf(" %s\n", mem_name); } else string_print(NIL_PTR, "%s%s\n", mem_name); break; default: string_print(NIL_PTR, "%s%s", mem_name, (verbose_flag ? " " : "\n")); string_print(NIL_PTR, "not recogised item %d\n", header.dbuf.typeflag); } flush(); } } skip_entry() { register int blocks = block_size(); while (blocks--) (void) mread(tar_fd, io_buffer, BLOCK_SIZE); } extract(file) register char *file; { register int fd; switch (header.dbuf.typeflag) { case '1': /* Link */ if (link(header.member.m_link, file) < 0) string_print(NIL_PTR, "Cannot link %s to %s\n", header.member.m_link, file); else if (verbose_flag) string_print(NIL_PTR, "Linked %s to %s\n", header.member.m_link, file); return; case '5': /* directory */ if (make_dir(file) == 0) { do_chown(file); verb_print("created directory", file); } /* no else: mkdir will print a message if it * fails */ return; case '3': /* character special */ case '4': /* block special */ { int dmajor, dminor, mode; dmajor = (int) convert(header.dbuf.devmajor, INT_TYPE); dminor = (int) convert(header.dbuf.devminor, INT_TYPE); mode = (header.dbuf.typeflag == '3' ? S_IFCHR : S_IFBLK); if (mknod(file, mode, (dmajor << 8 | dminor), 0) == 0) { if (verbose_flag) string_print(NIL_PTR, "made %s special file major %s minor %s\n", (header.dbuf.typeflag == '3' ? "character" : "block"), header.dbuf.devmajor, header.dbuf.devminor); do_chown(file); } return; } case '2': /* symbolic link */ case '7': /* contiguous file -- what is this (KS) */ print("Not implemented file type\n"); return; /* not implemented, but break out */ #ifdef HAVE_FIFO case '6': /* fifo */ if (mkfifo(file, 0) == 0) { /* is chmod'ed in do_chown */ do_chown(file); verb_print("made fifo", file); } else string_print(NIL_PTR, "Can't make fifo %s\n", file); return; #endif } /* Security change: creat with mode 0600, chown and then chmod -- KS */ if ((fd = creat(file, 0600)) < 0) { string_print(NIL_PTR, "Cannot create %s\n", file); return; } copy(file, tar_fd, fd, convert(header.member.m_size, LONG_TYPE)); (void) close(fd); do_chown(file); } do_chown(file) char *file; { int uid = -1, gid = -1; /* these are illegal ??? -- KS */ if (!chown_flag) { /* set correct owner and group -Dal */ if (header.dbuf.magic[TMAGLEN] == ' ') header.dbuf.magic[TMAGLEN] == '\0'; /* some tars out there * ... */ if (strncmp(TMAGIC, header.dbuf.magic, TMAGLEN)) { struct passwd *pwd, *getpwnam(); struct group *grp, *getgrnam(); pwd = getpwnam(header.dbuf.uname); if (pwd != NULL) uid = pwd->pw_uid; grp = getgrnam(header.dbuf.gname); if (grp != NULL) gid = grp->gr_gid; } if (uid == -1) uid = (int) convert(header.member.m_uid, INT_TYPE); if (gid == -1) gid = (int) convert(header.member.m_gid, INT_TYPE); chown(file, uid, gid); } chmod(file, u_mask & (int) convert(header.member.m_mode, INT_TYPE)); /* Should there be a timestamp if the chown failes? -- KS */ timestamp(file); } timestamp(file) char *file; { time_t times[2]; times[0] = times[1] = (long) convert(header.dbuf.mtime, LONG_TYPE); utime(file, times); } copy(file, from, to, bytes) char *file; int from, to; register long bytes; { register int rest; int blocks = (int) ((bytes + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE); if (verbose_flag) string_print(NIL_PTR, "%s, %d tape blocks\n", file, blocks); #ifdef DEBUG fprintf(stderr,"tar: copy %d blocks ...\n",blocks); flush(); #endif while (blocks--) { clear_iobuf(); rest = (bytes > (long) BLOCK_SIZE) ? BLOCK_SIZE : (int) bytes; mread(from, io_buffer, (from == tar_fd) ? BLOCK_SIZE : rest); mwrite(to, io_buffer, (to == tar_fd) ? BLOCK_SIZE : rest); bytes -= (long) rest; } } long convert(str, type) char str[]; int type; { register long ac = 0L; register int i; for (i = 0; i < type; i++) { if (str[i] >= '0' && str[i] <= '7') { ac <<= 3; ac += (long) (str[i] - '0'); } } return ac; } make_dir(dir_name) char *dir_name; { register int pid, w; int ret; /* Why not allow to mkdir(../directory)? -- changed now KS 2/10/89 */ if ((dir_name[0] == '.') && (dir_name[1] == '\0')) return 0; if ((pid = fork()) < 0) { perror("fork"); error("Cannot fork().", NIL_PTR); } if (pid == 0) { execl(MKDIR, "mkdir", dir_name, (char *) 0); perror("execl"); error("Cannot execute mkdir.", NIL_PTR); } do { w = wait(&ret); } while (w != -1 && w != pid); return ret; } checksum() { register char *ptr = header.member.m_checksum; register int ac = 0; while (ptr < &header.member.m_checksum[INT_TYPE]) *ptr++ = ' '; ptr = header.hdr_block; while (ptr < &header.hdr_block[BLOCK_SIZE]) ac += *ptr++; return ac; } is_dir(file) register char *file; { while (*file++ != '\0'); return(*(file - 2) == '/'); } char *path_name(file) register char *file; { string_print(pathname, "%s%s", path, file); return pathname; } add_path(name) register char *name; { register char *path_ptr = path; while (*path_ptr) path_ptr++; if (name == NIL_PTR) { while (*path_ptr-- != '/'); while (*path_ptr != '/' && path_ptr != path) path_ptr--; if (*path_ptr == '/') path_ptr++; *path_ptr = '\0'; } else { while (*name) { if (path_ptr == &path[NAME_SIZE]) error("Pathname too long", NIL_PTR); *path_ptr++ = *name++; } *path_ptr++ = '/'; *path_ptr = '\0'; } } /* * add a file to the archive */ add_file(file) register char *file; { int blksiz; struct stat st; char *linkname; register int fd = -1; char namebuf[16]; /* -Dal */ char cwd[129]; /* -KS */ char *getcwd(); /* marks@mgse Mon Sep 25 10:06:08 CDT 1989 */ char *is_linked(); if (stat(file, &st) < 0) { string_print(NIL_PTR, "Cannot find %s\n", file); return; } if (st.st_dev == ar_dev && st.st_ino == ar_inode) { string_print(NIL_PTR, "Cannot tar current archive file (%s)\n", file); return; } /* marks@mgse Mon Sep 25 12:06:28 CDT 1989 */ /* calculo los bloques, con 1 header, 1 blanco y 1 de ajuste y 1 por las dudas */ if ((st.st_mode & S_IFMT) != S_IFREG) blksiz = 5; /* los casos especiales son de 1 solo bloque */ else blksiz = ((int) (st.st_size / 512)) + 4; if (size != 0) { /* en caso de haber limite */ if ((blksiz+total_blocks) > maxlbn) { /* si se llego al limite */ adjust_boundary(); /* escribe fin de archivo */ flush(); dsknro++; sync(); /* do sync into disks */ sync(); sync(); sync(); sync(); fprintf(stderr,"\nPut disk %d and press ENTER.\n", dsknro); while (kbhit() == 0); /* espero ENTER */ total_blocks = 0; /* reset */ /* reabro tar file */ tar_fd = creat_fl ? creat(tar_file, 0644) : open(tar_file, 0); if (tar_fd < 0) { perror("tar_fd"); error("Cannot open ", tar_file); } } } if ((fd = add_open(file, &st)) < 0) { string_print(NIL_PTR, "Cannot open %s\n", file); return; } make_header(path_name(file), &st); if ((linkname = is_linked(&st)) != NULL) { strncpy(header.dbuf.linkname, linkname, NAMSIZ); header.dbuf.typeflag = '1'; if (verbose_flag) string_print(NIL_PTR, "linked %s to %s\n", header.dbuf.linkname, file); string_print(header.member.m_checksum, "%I ", checksum()); mwrite(tar_fd, (char *) &header, sizeof(header)); } else { is_added(&st, file); switch (st.st_mode & S_IFMT) { case S_IFREG: header.dbuf.typeflag = '0'; string_print(header.member.m_checksum, "%I ", checksum()); mwrite(tar_fd, (char *) &header, sizeof(header)); copy(path_name(file), fd, tar_fd, (long) st.st_size); break; case S_IFDIR: header.dbuf.typeflag = '5'; string_print(header.member.m_checksum, "%I ", checksum()); mwrite(tar_fd, (char *) &header, sizeof(header)); if (NULL == getcwd(cwd, 129)) string_print(NIL_PTR, "Error: cannot getcwd()\n"); else if (chdir(file) < 0) string_print(NIL_PTR, "Cannot chdir to %s\n", file); else { verb_print("read directory", file); add_path(file); #ifdef DIRECT_3 { DIR *dirp; struct direct *dp; struct stat dst; dirp = opendir("."); while (NULL != (dp = readdir(dirp))) if (strcmp(dp->d_name, ".") == 0) is_linked(&st); else if (strcmp(dp->d_name, "..") == 0) { if (stat("..", &dst) == 0) is_linked(&dst); } else { strcpy(namebuf, dp->d_name); add_file(namebuf); } closedir(dirp); } #else { int i; struct direct dir; struct stat dst; for (i = 0; i < 2; i++) { /* . and .. */ mread(fd, &dir, sizeof(dir)); if (strcmp(dir.d_name, ".") == 0) is_linked(&st); else if (strcmp(dir.d_name, "..") == 0) { if (stat("..", &dst) == 0) is_linked(&dst); } else break; } while (read(fd, &dir, sizeof(dir)) == sizeof(dir)) if (dir.d_ino) { strncpy(namebuf, dir.d_name, 14); namebuf[14] = '\0'; add_file(namebuf); } } #endif chdir(cwd); add_path(NIL_PTR); *file = 0; } break; #ifdef HAVE_FIFO case S_IFIFO: header.dbuf.typeflag = '6'; verb_print("read fifo", file); string_print(header.member.m_checksum, "%I ", checksum()); mwrite(tar_fd, (char *) &header, sizeof(header)); break; #endif case S_IFBLK: header.dbuf.typeflag = '4'; if (verbose_flag) { char sizebuf[TSIZLEN+1]; strncpy(sizebuf, header.dbuf.size, TSIZLEN); sizebuf[TSIZLEN] = 0; string_print(NIL_PTR, #ifdef _MINIX "read block device %s major %s minor %s size %s\n", #else "read block device %s major %s minor %s\n", #endif file, header.dbuf.devmajor, header.dbuf.devminor, sizebuf); } string_print(header.member.m_checksum, "%I ", checksum()); mwrite(tar_fd, (char *) &header, sizeof(header)); break; case S_IFCHR: header.dbuf.typeflag = '3'; if (verbose_flag) string_print(NIL_PTR, "read character device %s major %s minor %s\n", file, header.dbuf.devmajor, header.dbuf.devminor); string_print(header.member.m_checksum, "%I ", checksum()); mwrite(tar_fd, (char *) &header, sizeof(header)); break; default: is_deleted(&st); string_print(NIL_PTR, "Tar: %s unknown file type. Not added.\n", file); *file = 0; } } flush(); add_close(fd); } verb_print(s1, s2) char *s1, *s2; { if (verbose_flag) string_print(NIL_PTR, "%s: %s\n", s1, s2); } add_close(fd) int fd; { if (fd != 0) close(fd); } /* * open file 'file' to be added to archive, return file descriptor */ add_open(file, st) char *file; struct stat *st; { int fd; if (((st->st_mode & S_IFMT) != S_IFREG) && ((st->st_mode & S_IFMT) != S_IFDIR)) return 0; fd = open(file, O_RDONLY); return fd; } make_header(file, st) char *file; register struct stat *st; { register char *ptr = header.member.m_name; struct passwd *pwd, *getpwuid(); struct group *grp, *getgrgid(); clear_header(); while (*ptr++ = *file++); if ((st->st_mode & S_IFMT) == S_IFDIR) { /* fixed test -Dal */ *(ptr - 1) = '/'; } string_print(header.member.m_mode, "%I ", st->st_mode & 07777); string_print(header.member.m_uid, "%I ", st->st_uid); string_print(header.member.m_gid, "%I ", st->st_gid); if ((st->st_mode & S_IFMT) == S_IFREG) string_print(header.member.m_size, "%L ", st->st_size); else strncpy(header.dbuf.size, "0", TSIZLEN); string_print(header.member.m_time, "%L ", st->st_mtime); strncpy(header.dbuf.magic, TMAGIC, TMAGLEN); header.dbuf.version[0] = 0; header.dbuf.version[1] = 0; pwd = getpwuid(st->st_uid); strncpy(header.dbuf.uname, (pwd != NULL ? pwd->pw_name : "nobody"), TUNMLEN); grp = getgrgid(st->st_gid); strncpy(header.dbuf.gname, (grp != NULL ? grp->gr_name : "nobody"), TGNMLEN); if (st->st_mode & (S_IFBLK | S_IFCHR)) { string_print(header.dbuf.devmajor, "%I ", (st->st_rdev >> 8)); string_print(header.dbuf.devminor, "%I ", (st->st_rdev & 0xFF)); } header.dbuf.prefix[0] = 0; } is_added(st, file) struct stat *st; char *file; { struct link *new; char *name; char *malloc(); if ((*file == 0) || (st->st_nlink == 1)) return; name = path_name(file); new = (struct link *) malloc(sizeof(struct link)+strlen(name)); if (new == NULL) { print("Out of memory\n"); return; } new->next = link_top; new->dev = st->st_dev; new->ino = st->st_ino; new->nlink = st->st_nlink - 1; strcpy(new->name, name); link_top = new; } is_deleted(st) struct stat *st; { struct link *old; void free(); if ((old = link_top) != NULL) { link_top = old->next; free(old); } } char *is_linked(st) struct stat *st; { struct link *cur = link_top; struct link **pre = &link_top; static char name[NAMSIZ]; while (cur != NULL) if ((cur->dev != st->st_dev) || (cur->ino != st->st_ino)) { pre = &cur->next; cur = cur->next; } else { if (--cur->nlink == 0) { *pre = cur->next; strncpy(name, cur->name, NAMSIZ); return name; } return cur->name; } return NULL; } clear_header() { register char *ptr = header.hdr_block; while (ptr < &header.hdr_block[BLOCK_SIZE]) *ptr++ = '\0'; } clear_buffer() { register char *ptr = obuf; while (ptr < &obuf[MAX_BUF]) *ptr++ = '\0'; } clear_iobuf() { register char *ptr = io_buffer; while (ptr < &io_buffer[BLOCK_SIZE]) *ptr++ = '\0'; } adjust_boundary() { #ifdef DEBUG fprintf(stderr,"tar: adjusting boundary ...\n"); flush(); #endif clear_header(); mwrite(tar_fd, (char *) &header, sizeof(header)); #ifndef POSIX_COMP while (total_blocks++ < BLOCK_BOUNDARY) mwrite(tar_fd, (char *) &header, sizeof(header)); #else mwrite(tar_fd, (char *) &header, sizeof(header)); #endif while (blkptr != 0) /* fill buffer with blanks until writting */ mwrite(tar_fd, (char *) &header, sizeof(header)); (void) close(tar_fd); } /* mread1(fd, address, bytes) int fd, bytes; char *address; { if (read(fd, address, bytes) != bytes) error("Tar: read error.", NIL_PTR); } */ mread(fd, address, bytes) int fd, bytes; char *address; { char *ptr = address; /* read buffer */ char *ptr1; /* local buffer */ int byt = 0; #ifdef DEBUG fprintf(stderr,"tar: reading %d bytes ...\n",bytes); flush(); #endif if ((fd == tar_fd)&&((ext_fl==TRUE)||(show_fl==TRUE))) { /* read buffer if empty */ if (blkptr == bsize) { /* empty buffer */ #ifdef DEBUG fprintf(stderr,"tar: reading buffer, %d blocks ...\n",bsize); flush(); #endif /* clear_buffer(); /* clean buffer */ if (read(fd, obuf, (bsize*TBLOCK)) != (bsize*TBLOCK)) { perror("read"); error("Tar: read error.", NIL_PTR); } blkptr = 0; /* adjust pointer */ } ptr1 = &obuf[blkptr*TBLOCK]; while(byt++ < bytes) /* copy buffer to external buffer */ *ptr++ = *ptr1++; blkptr++; /* update pointer */ } else { #ifdef DEBUG fprintf(stderr,"tar: normal read buffer ...\n"); flush(); #endif if (read(fd, address, bytes) != bytes) { perror("read"); error("Tar: read error.", NIL_PTR); } } } /* mwrite1(fd, address, bytes) int fd, bytes; char *address; { if (write(fd, address, bytes) != bytes) error("Tar: write error.", NIL_PTR); total_blocks++; } */ mwrite(fd, address, bytes) int fd, bytes; char *address; { char *ptr = address; char *ptr1; int byt = 0; if ((fd == tar_fd) && (creat_fl == TRUE)) { /* use buffer to hold blocks until reach size */ ptr1 = &obuf[blkptr*TBLOCK]; while(byt++ < bytes) /* copy info to buffer */ *ptr1++ = *ptr++; blkptr++; /* update pointer */ if (blkptr == bsize) { /* if we reach buffer limit */ #ifdef DEBUG fprintf(stderr,"tar: flushing buffer ...\n"); flush(); sync(); #endif sync(); if (write(fd, obuf, (bsize*TBLOCK)) != (bsize*TBLOCK)) { perror("write"); error("Tar: write error.", NIL_PTR); } total_blocks += bsize; /* update block count */ blkptr = 0; /* clean pointer */ clear_buffer(); /* clean buffer */ } } else { #ifdef DEBUG fprintf(stderr,"tar: normal write buffer ...\n"); flush(); sync(); #endif if (write(fd, address, bytes) != bytes) { perror("write"); error("Tar: write error.", NIL_PTR); } total_blocks++; } } char output[BLOCK_SIZE]; print(str) /* changed to use stderr rather than stdout * -Dal */ register char *str; { static int index = 0; if (str == NIL_PTR) { write(2, output, index); index = 0; return; } while (*str) { output[index++] = *str++; if (index == BLOCK_SIZE) { write(2, output, BLOCK_SIZE); index = 0; } } } char *num_out(number) register long number; { static char num_buf[12]; register int i; for (i = 11; i--;) { num_buf[i] = (number & 07) + '0'; number >>= 3; } return num_buf; } /*VARARGS2*/ string_print(buffer, fmt, args) char *buffer; register char *fmt; int args; { register char *buf_ptr; char *scan_ptr; char buf[NAME_SIZE]; char *argptr = (char *) &args; BOOL pr_fl, i; if (pr_fl = (buffer == NIL_PTR)) buffer = buf; buf_ptr = buffer; while (*fmt) { if (*fmt == '%') { fmt++; switch (*fmt++) { case 's': scan_ptr = *((char **) argptr); argptr += sizeof(char *); break; case 'I': scan_ptr = num_out((long) *((int *) argptr)); argptr += sizeof(int); for (i = 0; i < 5; i++) scan_ptr++; break; case 'L': scan_ptr = num_out(*((long *) argptr)); argptr += sizeof(long); break; case 'd': scan_ptr = num_out((long) *((int *) argptr)); argptr += sizeof(int); while (*scan_ptr == '0') scan_ptr++; scan_ptr--; break; default: scan_ptr = ""; } while (*buf_ptr++ = *scan_ptr++); buf_ptr--; } else *buf_ptr++ = *fmt++; } *buf_ptr = '\0'; if (pr_fl) print(buffer); } /* retorna 1 si la tecla presionada en stdin en ENTER, sino cero */ int kbhit() { int c; c = getchar(); /* get a char from stdin */ if (c != (int)'\n') return(0); else return(1); } #ifdef MINIX #define YEAR (365L * 24L * 3600L) #define LYEAR (366L * 24L * 3600L) int mo[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; long curtime; char *moname[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; char datestr[25]; /* string where put the formated date */ char *date(t) long t; /* time in seconds */ { /* Print the date. This only works from 1970 to 2099. */ int i, year, day, month, hour, minute; long length, time(), original; for (i = 0; i< 25;i++) datestr[i] = '\0'; /* clear date buffer */ year = 1970; original = t; while (t > 0) { length = (year % 4 == 0 ? LYEAR : YEAR); if ( t < length) break; t -= length; year++; } /* Year has now been determined. Now the rest. */ day = t/(24L * 3600L); t -= (long) day * 24L * 3600L; hour = t/3600L; t -= 3600L * (long) hour; minute = (int) t/60L; /* Determine the month and day of the month. */ mo[1] = (year % 4 == 0 ? 29 : 28); month = 0; i = 0; while (day >= mo[i]) { month++; day -= mo[i]; i++; } /* At this point, 'year', 'month', 'day', 'hour', 'minute' ok */ if (curtime == 0) curtime = time( (long*)0); /* approximate current time */ sprintf(&datestr[0], "%5d %3s %2d ",year, moname[month], day+1); if (hour < 10) sprintf(&datestr[13], "0%d:",hour); else sprintf(&datestr[13], "%2d:",hour); if (minute < 10) sprintf(&datestr[16], "0%d ",minute); else sprintf(&datestr[16], "%2d ",minute); return(datestr); /* retorno string */ } #endif