|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - download
Length: 20180 (0x4ed4) Types: TextFile Notes: UNIX file Names: »cpdir.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦f4b8d8c84⟧ UNIX Filesystem └─ ⟦this⟧ »cmd/cpdir.c«
/* * Cpdir. Copy hierarchies in a file system, preserving structure. * Define SLOW for 'block at a time copying' - not recommended tho. */ #include <stdio.h> #include <errno.h> #include <signal.h> #include <types.h> #include <stat.h> #include <dir.h> #include <ctype.h> #include <access.h> #include <canon.h> #define CPBUFSIZ (50*BUFSIZ) /* copy buffer */ #define SDSIZ (sizeof(struct direct)) #define MAXINT 32767 #define SOURCE 0 #define TARGET 1 #define DEV0 ((dev_t)0) #define INODE0 ((ino_t)0) #define ROOTUID 0 #define HASHSIZE 37 #define hash(ino) ((ino)%HASHSIZE) #define not ! #define and && #define or || #define TRUE (0==0) #define FALSE (not TRUE) typedef char bool; typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned long ulong; typedef union { struct direct dbuf; char cbuf[SDSIZ + 1]; } DIRBUF; #define db_ino dbuf.d_ino #define db_name dbuf.d_name typedef struct link { ino_t l_ino; dev_t l_dev; dev_t l_tdev; struct link *l_next; uint l_nlink; char l_name[]; } LINK; typedef struct str { struct str *s_next; char s_str[]; } STR; /* * Global variables. */ extern int errno; dev_t dir2_dev; ino_t dir2_ino; dev_t src_dev; dev_t tgt_dev; struct stat srcstat; struct stat tgtstat; LINK *srctab[HASHSIZE]; LINK *tgttab[HASHSIZE]; STR *strtab[HASHSIZE]; bool dflag; bool eflag; bool sflag; bool tflag; bool uflag; bool vflag; bool wflag; bool root; bool splitmsg; bool errprefix; bool dir1slash; bool dir2slash; int rlimit = MAXINT; int rlevel = 1; int exitval; int numsuppress; int broken; int srclinks; int tgtlinks; int srcsize; int tgtsize; int dir1len; int dir2len; short uid; short gid; char *dir1; char *dir2; char *target; char *source; /* * Error message and other strings. */ char *dot = "."; char *dotdot = ".."; char *fmt1 = "%s\n"; char *fmt2 = "%s: %s\n"; char *fmt3 = "%s: %s%s\n"; char *fmt4 = "%s: %s%s%s\n"; char *usage = "usage: cpdir [options] dir1 dir2"; char *devbound = "crossing device boundary"; char *writeerr = "write error"; char *readerr = "read error"; char *supressd = "suppressed"; char *linked = "linked"; char *notdir = "not a directory"; char *nomemory = "out of memory"; char *nopermit = "permission denied"; char *nomkdir = "cannot make directory"; char *nounlink = "cannot unlink "; char *nolink = "cannot link "; char *nocreate = "cannot create"; char *nofind = "cannot find"; char *noopen = "cannot open"; /* * Functions returning non_int. */ long lseek(); char *tmalloc(); char *realloc(); char *rindex(); char *concat(); char *parent(); bool suppress(); bool isslash(); bool dirchecks(); bool linkattempt(); bool tgtunlink(); LINK *linklocate(); LINK *linkinstall(); main(ac, av) int ac; char *av[]; { ac = 0; aarghh(av); init(); cpdir(); report(); return (exitval); } aarghh(av) register char *av[]; { register char *cp; for (;;) { if ((cp = *++av) == NULL) { errprint(fmt1, usage); exit(1); } if (*cp++ != '-') break; while (*cp) switch (*cp++) { case 'd': dflag = TRUE; break; case 'e': eflag = TRUE; break; case 'r': if (not isdigit(*cp)) { rlimit = 1; break; } *av = cp; rlimit = new_atoi(av); cp = *av; break; case 's': sflag = TRUE; strinstall(cp); ++numsuppress; goto NEXTARG; break; case 't': tflag = wflag = eflag = TRUE; break; case 'u': uflag = TRUE; break; case 'v': vflag = wflag = TRUE; break; default: errprint(fmt1, usage); exit(1); } NEXTARG:; } if ((dir1=*av++) == NULL or (dir2=*av++) == NULL or *av != NULL) { errprint(fmt1, usage); exit(1); } return; } /* * Actions: Initialize certain flags and the name buffers source and target. * Verify dir1 and dir2 permissions. Dir2 is made if necessary. * Side Effects: On return srcstat contains the status of dir1. If dir2 * exists tgtstat contains its status. In the case that tflag is set and dir2 * does not exist, tgt_dev and tgtstat.st_dev are the device of target's * parent, the device that target would be created on except for tflag. */ init() { struct stat outstat; struct stat errstat; uid = getuid(); gid = getgid(); root = (uid == ROOTUID); if (root) umask(0); errprefix = not isatty(fileno(stdin)); fstat(fileno(stdout), &outstat); fstat(fileno(stderr), &errstat); splitmsg = (outstat.st_ino != errstat.st_ino) or (outstat.st_dev != errstat.st_dev); dir1len = strlen(dir1); srcsize = (dir1len + 1); source = tmalloc(srcsize); strcpy(source, dir1); dir1slash = isslash(dir1); dir2len = strlen(dir2); tgtsize = dir2len + 1; target = tmalloc(tgtsize); strcpy(target, dir2); dir2slash = isslash(dir2); /* * We must check boundary conditions before we call dirchecks(). * These are: source exists and is a directory, and if target does not * exist that we have write and search permission on its parent and * tgt_dev contains the device number of the parent. */ if (stat(dir1, &srcstat) < 0) { errprint(fmt2, dir1, nofind); exit(1); } if ((srcstat.st_mode & S_IFMT) != S_IFDIR) { errprint(fmt2, dir1, notdir); exit(1); } if (access(dir2, 0) < 0) { register char *cp; cp = parent(dir2); if (stat(cp, &tgtstat) < 0) { errprint(fmt2, dir2, "cannot find parent"); exit(1); } if (access(cp, AWRITE|ASRCH) < 0) { errprint(fmt2, dir2, nomkdir); exit(1); } tgt_dev = tgtstat.st_dev; } if (not dirchecks()) exit(1); src_dev = srcstat.st_dev; dir2_dev = tgt_dev = tgtstat.st_dev; if (access(target, 0) == 0) dir2_ino = tgtstat.st_ino; return; } /* * Assume: Source is a directory we can read and search. Srcstat contains its * status. Target is a directory we can write and search. Tgtstat contains its * status. Src_dev and tgt_dev are the devices of the parents of source and * target. If tflag is on target may not exist. In this case tgtstat.st_dev * contains the device where target would have been created if tflag were off. */ cpdir() { register int n; register int fd; register DIRBUF *dbp; DIRBUF dirbuf; long address; dev_t sdev; dev_t tdev; struct stat locsrcstat; struct stat loctgtstat; /* * Check for circular copy. */ if (srcstat.st_ino == dir2_ino and srcstat.st_dev == dir2_dev) { if (wflag) printf(fmt2, source, "not copied to avoid circular copy"); return; } /* * Open source, and prepare dirbuf. */ if ((fd = open(source, 0)) < 0) { errprint(fmt2, source, noopen); return; } dbp = &dirbuf; dbp->cbuf[SDSIZ] = '\0'; /* * Save srcstat, src_dev, tgtstat, tgt_dev to restore before exit. */ locsrcstat = srcstat; loctgtstat = tgtstat; tdev = tgt_dev; sdev = src_dev; /* * Detect the crossing of device boundaries. */ if (tgt_dev != tgtstat.st_dev) { if (wflag) printf(fmt2, target, devbound); tgt_dev = tgtstat.st_dev; } if (src_dev != srcstat.st_dev) { if (wflag) printf(fmt2, source, devbound); src_dev = srcstat.st_dev; } /* * Loop through the directory source. */ while ((n = read(fd, dbp->cbuf, SDSIZ)) > 0) { if (n != SDSIZ) { errprint(fmt2, source, readerr); close(fd); goto OUT; } canino(dbp->db_ino); if (dbp->db_ino == INODE0 or strcmp(dbp->db_name, dot) == 0 or strcmp(dbp->db_name, dotdot) == 0) continue; grow(dbp->db_name); if (sflag and suppress()) { if (vflag) printf(fmt2, source, supressd); shrink(); continue; } if (stat(source, &srcstat) < 0) { errprint(fmt2, source, nofind); shrink(); continue; } switch (srcstat.st_mode & S_IFMT) { default: errprint(fmt2, source, "unknown file type"); shrink(); continue; case S_IFCHR: case S_IFBLK: cpnode(); shrink(); continue; case S_IFPIP: srcstat.st_rdev = DEV0; cpnode(); shrink(); continue; case S_IFREG: cpfile(); shrink(); continue; case S_IFDIR: if (rlevel == rlimit) { if (vflag) printf(fmt2, source, supressd); shrink(); continue; } if (not dirchecks()) { shrink(); continue; } address = lseek(fd, 0L, 1); close(fd); ++rlevel; cpdir(); --rlevel; shrink(); if ((fd = open(source, 0)) < 0) { errprint(fmt2, source, "cannot reopen, copy incomplete"); goto OUT; } lseek(fd, address, 0); continue; } } close(fd); OUT: if (vflag) printf(fmt1, target); srcstat = locsrcstat; tgtstat = loctgtstat; tgt_dev = tdev; src_dev = sdev; adjust(); return; } /* * Assume source names the file to be copied, srcstat contains its status, * and target is the file to copy to. Target may or may not exist. */ cpfile() { #ifndef SLOW register char *ip; #endif register int n; register int i; register int fd1; register int fd2; register size_t size; #ifndef SLOW register char *wp; /* write pointer */ register int wflag; /* write flag */ static char buf[CPBUFSIZ]; /* copy buffer */ #else static char buf[BUFSIZ]; #endif if (access(source, AREAD) < 0) { errprint(fmt2, source, noopen); return; } if (uflag and stat(target, &tgtstat) == 0) if (srcstat.st_mtime <= tgtstat.st_mtime) { if (vflag) printf(fmt2, target, "no update"); return; } if (not tgtunlink()) return; if (srcstat.st_nlink > 1) if (linkattempt()) return; if (tflag) { if (vflag) printf(fmt1, target); return; } if ((fd1 = open(source, 0)) < 0) { errprint(fmt2, source, noopen); return; } if ((fd2 = creat(target, 0)) < 0) { close(fd1); errprint(fmt2, target, nocreate); return; } size = srcstat.st_size; #ifndef SLOW while ((n = read(fd1, buf, sizeof(buf))) > 0) { #else while ((n = read(fd1, buf, BUFSIZ)) > 0) { #endif /* * Check for blocks of zeroes (holes in a sparse file). * However, a block of zeroes at the end of a file must be * written so the file has correct length. */ #ifndef SLOW wp = ip = buf; size -= n; while ((n-BUFSIZ) > 0 || ((n-BUFSIZ) == 0 && size != 0)) { n -= BUFSIZ; wflag = FALSE; ip = wp; for (i = 0; i < BUFSIZ; ++i) if (*ip++ != '\0') { wflag = TRUE; break; } if (wflag) { if (write(fd2, wp, BUFSIZ) < BUFSIZ) { errprint(fmt2, target, writeerr); close(fd1); close(fd2); return (FALSE); } } else { lseek(fd2, (long)BUFSIZ, 1); } wp += BUFSIZ; } if (write(fd2, wp, n) < n) { errprint(fmt2, target, writeerr); close(fd1); close(fd2); return (FALSE); } #else if ((size -= n) == (size_t)0) goto WRITE; for (i = 0; i < n; ++i) if (buf[i] != '\0') goto WRITE; lseek(fd2, (long)BUFSIZ, 1); continue; WRITE: if (write(fd2, buf, n) < n) { errprint(fmt2, target, writeerr); close(fd1); close(fd2); return (FALSE); } #endif } close(fd1); close(fd2); adjust(); if (n < 0) errprint(fmt2, source, readerr); else if (vflag) printf(fmt1, target); return; } /* * Copy special nodes and named pipes. */ cpnode() { if (not root) { if (vflag) printf(fmt2, source, "not the super-user"); return; } if (not tgtunlink()) return; if (srcstat.st_nlink > 1) if (linkattempt()) return; if (not tflag) { if (mknod(target, srcstat.st_mode, srcstat.st_rdev) < 0) { errprint(fmt2, target, "cannot make node"); return; } adjust(); } if (vflag) printf(fmt2, target, "copied node"); return; } /* * Grow each of target and source by appending '/' and cp. */ grow(cp) register char *cp; { register int a; register int b; a = strlen(cp) + 2; b = strlen(source); if (a+b > srcsize) { if ((source = realloc(source, a+b)) == NULL) { errprint(fmt1, nomemory); exit(1); } srcsize = a+b; } if (rlevel > 1 or not dir1slash) source[b++] = '/'; strcpy(source+b, cp); b = strlen(target); if (a+b > tgtsize) { if ((target = realloc(target, a+b)) == NULL) { errprint(fmt1, nomemory); exit(1); } tgtsize = a+b; } if (rlevel > 1 or not dir2slash) target[b++] = '/'; strcpy(target+b, cp); } /* * Shrink source and target down by the last pathname component. */ shrink() { if (rlevel == 1 and dir1slash) source[dir1len] = '\0'; else *rindex(source, '/') = '\0'; if (rlevel == 1 and dir2slash) target[dir2len] = '\0'; else *rindex(target, '/') = '\0'; return; } /* * Returns a value equal to atoi(*cpp). Positive decimal integers only. * Leaves *cpp pointing to the character that terminated the digit string. */ new_atoi(cpp) char **cpp; { register int sum = 0; register int c; register char *cp; sum = 0; cp = *cpp; while (isdigit(c = *cp++)) { sum *= 10; sum -= '0'; sum += c; } *cpp = cp-1; return (sum); } errprint(arg) char *arg[]; { register char *format; format = (errprefix) ? "cpdir: %r" : "%r"; fprintf(stderr, format, &arg); if (wflag and splitmsg) printf(format, &arg); if (not eflag) exit(1); exitval = 1; return; } /* * Interface to malloc to check for bad returns. */ char * tmalloc(n) int n; { register char *cp; if ((cp = malloc(n)) == NULL) { errprint(fmt1, nomemory); exit(1); } return (cp); } /* * Assume: source is a dir, srcstat has its status, we have write and search * permission on target's parent directory, tgt_dev is the device of target's * parent. * Actions: Check permissions on source. Check permissions on target if it * exists. If non-extant and tflag is off, make it. * Side Effects: On return, if target exists, tgtstat contains its status. If * it does not exist, tgtstat.st_dev is the device it would have been created * on if tflag were off. * Return TRUE if everything Aok, FALSE otherwise. */ bool dirchecks() { register int n; static int status; if (access(source, AREAD | ASRCH) < 0) { errprint(fmt2, source, nopermit); return (FALSE); } if (stat(target, &tgtstat) == 0) { if ((tgtstat.st_mode & S_IFMT) != S_IFDIR) { errprint(fmt3, source, "target is ", notdir); return (FALSE); } if (access(target, AWRITE | ASRCH) < 0) { errprint(fmt2, target, nopermit); return (FALSE); } return (TRUE); } if (tflag) { tgtstat.st_dev = tgt_dev; return (TRUE); } if ((n = fork()) < 0) { errprint(fmt2, target, nomkdir); return (FALSE); } if (n == 0) { close(2); umask(077); execl("/bin/mkdir", "cpdir", target, NULL); exit(1); } while (wait(&status) != n) ; if (status != 0) { errprint(fmt2, target, nomkdir); return (FALSE); } if (stat(target, &tgtstat) < 0) { errprint(fmt2, target, "made but cannot stat"); return (FALSE); } return (TRUE); } strinstall(cp) register char *cp; { register STR *sp; register int n; n = strhash(cp); for (sp = strtab[n]; sp != NULL; sp = sp->s_next) if (strcmp(cp, sp->s_str) == 0) return; sp = (STR *) tmalloc(sizeof(STR) + strlen(cp) + 1); sp->s_next = strtab[n]; strtab[n] = sp; strcpy(sp->s_str, cp); return; } bool suppress() { register char *cp; register STR *sp; register STR **spp; cp = source + dir1len; if (not dir1slash) ++cp; spp = strtab + strhash(cp); if ((sp = *spp) == NULL) return (FALSE); while (sp != NULL) { if (strcmp(cp, sp->s_str) == 0) { *spp = sp->s_next; free(sp); if (--numsuppress == 0) sflag = FALSE; return (TRUE); } spp = &(sp->s_next); sp = *spp; } return (FALSE); } strhash(cp) register uchar *cp; { register uint sum = 0; for (sum = 0; *cp != '\0'; sum += *cp++) ; return (sum % HASHSIZE); } LINK * linklocate(flag) int flag; { register LINK *lp; register struct stat *stp; if (flag == SOURCE) { stp = &srcstat; lp = srctab[hash(stp->st_ino)]; } else { stp = &tgtstat; lp = tgttab[hash(stp->st_ino)]; } for ( ; lp != NULL; lp = lp->l_next) { if (lp->l_ino != stp->st_ino) continue; if (lp->l_dev != stp->st_dev) continue; --(lp->l_nlink); return (lp); } return (NULL); } LINK * linkinstall(flag) int flag; { register LINK **lpp; register LINK *lp; register struct stat *stp; if (flag == SOURCE) { ++srclinks; stp = &srcstat; lpp = hash(stp->st_ino) + srctab; } else { ++tgtlinks; stp = &tgtstat; lpp = hash(stp->st_ino) + tgttab; } lp = (LINK *) tmalloc(sizeof(LINK) + strlen(target) - dir2len + 1); if (!dir2slash) strcpy(lp->l_name, target + dir2len + 1); else strcpy(lp->l_name, target + dir2len); lp->l_dev = stp->st_dev; lp->l_ino = stp->st_ino; lp->l_tdev = tgt_dev; lp->l_nlink = stp->st_nlink - 1; lp->l_next = *lpp; *lpp = lp; return (lp); } linkpurge(flag, lp) int flag; register LINK *lp; { register LINK *lp1; register LINK **lpp; if (flag == SOURCE) { --srclinks; lpp = hash(srcstat.st_ino) + srctab; } else { --tgtlinks; lpp = hash(tgtstat.st_ino) + tgttab; } for (lp1 = *lpp; lp1 != NULL; *lpp = lp1, lp1 = lp1->l_next) { if (lp1 != lp) continue; *lpp = lp1->l_next; free((char *)lp1); return; } return; } bool linkattempt() { register LINK *lp; register bool ret; register char *cp; if ((lp = linklocate(SOURCE)) == NULL) { linkinstall(SOURCE); return (FALSE); } cp = concat(dir2, lp->l_name); if (tflag) { if (tgt_dev != lp->l_tdev) { errprint(fmt4, target, nolink, " to ", cp); ++broken; ret = FALSE; } else { if (vflag) printf(fmt1, target); ret = TRUE; } } else if (link(cp, target) == 0) { if (vflag) printf(fmt1, target); ret = TRUE; } else { errprint(fmt4, target, nolink, " to ", cp); ++broken; ret = FALSE; } if (lp->l_nlink == 0) linkpurge(SOURCE, lp); return (ret); } bool tgtunlink() { register LINK *lp; if (stat(target, &tgtstat) < 0) return (TRUE); if ((tgtstat.st_mode & S_IFMT) == S_IFDIR) { errprint(fmt3, target, nounlink, "directory"); return (FALSE); } else if ((tgtstat.st_mode & S_IFMT) != (srcstat.st_mode & S_IFMT)) { errprint(fmt3, source, "file type mismatch with ", target); return (FALSE); } if (not tflag) if (unlink(target) < 0) { errprint(fmt2, target, nounlink); return (FALSE); } if (tflag) printf(fmt2, target, "unlinked"); if ((lp = linklocate(TARGET)) != NULL) { if (lp->l_nlink == 0) linkpurge(TARGET, lp); } else if (tgtstat.st_nlink > 1) linkinstall(TARGET); return (TRUE); } report() { register LINK *lp; register LINK **lpp; static char *fmtlinks = "%s external links into hierarchy %s:\n"; if (not wflag) return; if (broken) printf("%d internal link%s broken\n", broken, (broken == 1) ? "" : "s"); if (srclinks) { printf(fmtlinks, "missed", dir1); for (lpp = srctab; lpp < srctab + HASHSIZE; ++lpp) for (lp = *lpp; lp != NULL; lp = lp->l_next) printf("\t%d\t%s (inode %d)\n", lp->l_nlink, concat(dir1, lp->l_name), lp->l_ino); } if (tgtlinks) { printf(fmtlinks, "broken", target); for (lpp = tgttab; lpp < tgttab + HASHSIZE; ++lpp) for (lp = *lpp; lp != NULL; lp = lp->l_next) printf("\t%d\t%s (inode %d)\n", lp->l_nlink, concat(dir2, lp->l_name), lp->l_ino); } return; } /* * Chown, chmod, and chdate target. Assume srcstat has status of source, * and tgtstat has status of target if it exists. */ adjust() { time_t date[2]; if (tflag) return; if (root) chown(target, srcstat.st_uid, srcstat.st_gid); chmod(target, srcstat.st_mode & (root ? 07777 : 06777)); if (dflag) { time(&date[0]); date[1] = srcstat.st_mtime; utime(target, date); } return; } /* * Concatenate pieces of pathnames, a and b. If a is not "/" a '/' char * is placed between the names. Previous return is freed. */ char * concat(a, b) register char *a; char *b; { static char *ret; register int a1; register char *rp; if (ret != NULL) free(ret); a1 = strlen(a); rp = ret = tmalloc(a1 + strlen(b) + 2); strcpy(rp, a); rp += a1; if (not isslash(ret)) *rp++ = '/'; strcpy(rp, b); return (ret); } char * parent(cp) register char *cp; { static char *ret; register char *cp0; if (ret != NULL) free(ret); if ((cp0 = rindex(cp, '/')) == NULL) return (dot); ret = tmalloc(cp0 - cp + 1); strncpy(ret, cp, cp0 - cp); return (ret); } bool isslash(cp) register char *cp; { register int c; while ((c = *cp++) != '\0') if (c != '/') return (FALSE); return (TRUE); }