📄 synctree.c
字号:
/* synctree 4.15 - Synchronise file tree. Author: Kees J. Bot * 5 Apr 1989 * SYNOPSYS * synctree [-iuf] [[user1@machine1:]dir1 [[user2@]machine2:]dir2 * * Dir2 will then be synchronized to dir1 with respect to the flags. * The flags mean: * -i Be interactive on files other than directories too. * -u Only install files that are newer, i.e. that need an update. * -f Force. Don't ask for confirmation, all answers are 'yes'. * * Hitting return lets synctree use its proposed answer. Hitting CTRL-D is * like typing return to all questions that follow. * * If either of the directories to be synchronized contains the file ".backup" * then it is a backup directory. The file ".backup" in this directory is * an array of mode information indexed on inode number. * * Copyright 1989 Kees J. Bot, All rights reserved. * This program may only be used with Minix, unless permission has been * granted by me. Any other use is a violation of my copyright. * * 89/04/05, Kees J. Bot - Birth of tree synchronizing program. * 92/02/02 - General overhaul, rcp(1) like syntax. */#define nil 0#include <sys/types.h>#include <stdio.h>#include <sys/stat.h>#include <utime.h>#include <string.h>#include <signal.h>#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <stdlib.h>#include <unistd.h>#include <time.h>#include <sys/wait.h>#if _MINIX#include "limits.h"#include "minix/config.h"#define BLOCK_SIZE 1024#define LITTLE_ENDIAN (CHIP == INTEL)#define USE_SHADOWING (CHIP == M68000)#else#define LITTLE_ENDIAN 0#define USE_SHADOWING 0#endif#ifndef PATH_MAX#define PATH_MAX 1024#endif#ifndef S_ISLNK/* There were no symlinks in medieval times. */#define S_ISLNK(mode) (0)#define lstat stat#define symlink(path1, path2) (errno= ENOSYS, -1)#define readlink(path, buf, len) (errno= ENOSYS, -1)#endif#define NUMBYTES 4 /* Any number fits in this many bytes. */#define CHUNK 4096 /* Transfer files in this size chunks. */static int install= 0; /* Install files, do not delete, update if newer. */static int interact= 0; /* Ask permission to install too. */static int force= 0; /* Force trees to be completely equal. */static int backup= 0; /* This tree is for backup. */static char SYNCNAME[] = "synctree";static char SLAVENAME[] = "==SLAVE==";static char MASTERNAME[]= "==MASTER==";static char BACKUP[] = ".backup"; /* Backup filemodes. */static int filemodes; /* Filemodes fildes. */static int chan[2]= { 0, 1 }; /* In and output channel to opposite side. */#define BUCKSIZE (1+NUMBYTES+CHUNK)static char bucket[BUCKSIZE]; /* Put a lot of things here before sending. */static char *buckp= bucket; /* Fill pointer. */static int buckn= 0; /* # bytes in bucket. */enum orders { /* What back breaking labour should the slave perform? */ ENTER= 128, /* Make ready to process contents of directory. */ ADVANCE, /* Determine next pathname and report it back. */ CAT, /* Send contents of file. */ MORE, /* Send more file contents. */ CANCEL, /* Current pathname is not installed, remove as link. */ DIE, /* Die with exit(0); */ DIE_BAD, /* exit(1); */ POSITIVE, /* Ask a yes/no question expecting yes. */ NEGATIVE, /* Same expecting no. */ PASS_YES, /* Pass this to the master will you. */ PASS_NO /* Same here. */};#ifdef DEBUGchar *ORDERS[]= { "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD", "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO"};#endifenum answers { PATH= 128, /* Report pathname, and stat(2) info. */ LINK, /* Report linkname, pathname, and stat(2) info. */ DATA, /* Contents of file follow. */ NODATA, /* Can't read file. */ DONE, /* Nothing more to advance to. */ SYMLINK, /* Report symlinkname, pathname, and stat(2) info. */ YES, NO /* Result of an ASK. */};#ifdef DEBUGchar *ANSWERS[]= { "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO"};#define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg)#else#define DPRINTF(format, arg)#endifstruct mode { unsigned short md_mode; unsigned short md_uid; unsigned short md_gid; unsigned short md_rdev; unsigned short md_devsiz;};static char *arg0; /* basename(argv[0]) */static int ex= 0; /* exit status. */static void because(){ fprintf(stderr, ": %s\n", strerror(errno)); ex= 1;}static void perr(label) char *label;{ fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno)); ex= 1;}static void perrx(label) char *label;{ perr(label); exit(1);}#if S_HIDDEN/* Support for per achitecture hidden files. */static int transparent= 0;static void isvisible(name) char *name;{ char *p= name + strlen(name); while (p > name && *--p == '/') {} if (p > name && *p == '@' && p[-1] != '/') transparent= 1;}#else#define transparent 0#define isvisible(name) ((void) 0)#endifstatic void isbackup(slave) int slave;{ if ((filemodes= open(BACKUP, slave ? O_RDONLY : O_RDWR)) >= 0) backup= 1; else { if (errno != ENOENT) perrx(BACKUP); }}static char path[PATH_MAX+1]; /* Holds pathname of file being worked on. */static char lnkpth[PATH_MAX+1]; /* (Sym)link to path. */static char *linkpath; /* What path is, or should be linked to. */static struct stat st; /* Corresponding stat(2) info. */static char Spath[PATH_MAX+1]; /* Slave is looking at this. */static char Slnkpth[PATH_MAX+1];/* (Sym)link to Spath. */static char *Slinkpath=nil; /* Either nil or Slnkpth. */static struct stat Sst; /* Slave's stat(2). */static char *addpath(p, n) char *p, *n;/* Add a name to the path, return pointer to end. */{ if (p - path + 1 + strlen(n) > PATH_MAX) { *p= 0; fprintf(stderr, "%s: %s/%s is too long.\n", arg0, path, n); fprintf(stderr, "%s: Unable to continue.\n", arg0); exit(1); } if (p == path+1 && path[0] == '.') p= path; if (p > path) *p++ = '/'; while (*n != 0) *p++ = *n++; *p= 0; return p;}struct entry { /* A directory entry. */ struct entry *next; /* Next entry in same directory */ struct entry *dir; /* It is part of this directory */ struct entry *con; /* If a dir, its contents */ char *name; /* Name of this dir entry */};static struct entry *E= nil; /* File being processed. */static void setpath(e) struct entry *e;/* Set path leading to e. */{ static char *pend; if (e == nil) pend= path; else { setpath(e->dir); pend= addpath(pend, e->name); }}static void sort(ae) struct entry **ae;/* This is either a stable mergesort, or thermal noise, I'm no longer sure. * It must be called like this: if (L!=nil && L->next!=nil) sort(&L); */{ /* static */ struct entry *e1, **mid; /* Need not be local */ struct entry *e2; e1= *(mid= &(*ae)->next); do { if ((e1= e1->next) == nil) break; mid= &(*mid)->next; } while ((e1= e1->next) != nil); e2= *mid; *mid= nil; if ((*ae)->next != nil) sort(ae); if (e2->next != nil) sort(&e2); e1= *ae; for (;;) { if (strcmp(e1->name, e2->name)<=0) { if ((e1= *(ae= &e1->next)) == nil) { *ae= e2; break; } } else { *ae= e2; e2= *(ae= &e2->next); *ae= e1; if (e2 == nil) break; } }}static void enter()/* Collect directory entries of E. */{ struct entry **last= &E->con, *new; struct dirent *e; DIR *d; if ((d= opendir(path)) == nil) { fprintf(stderr, "%s: Can't read dir %s\n", arg0, path); return; } while ((e= readdir(d)) != nil) { if (e->d_name[0] == '.' && (e->d_name[1] == 0 || (e->d_name[1] == '.' && e->d_name[2] == 0) )) continue; new= (struct entry *) malloc(sizeof(*new)); new->next= nil; new->dir= E; new->con= nil; new->name= (char *) malloc(strlen(e->d_name) + 1); strcpy(new->name, e->d_name); *last= new; last= &new->next; } closedir(d); if (E->con != nil && E->con->next != nil) sort(&E->con);}#define cancellink() ((void) islink()) /* Delete link by calling for it twice. (Gross hack) */static char *islink()/* Returns the name of the file path is linked to. If no such link can be * found, then path is added to the list and nil is returned. If all the * links of a file have been seen, then it is removed from the list. * Directories are not seen as linkable. */{ struct links { struct links *next; /* They form a simple list. */ char *name; /* Full name of the link. */ dev_t dev; /* Identification. */ ino_t ino; int count; /* This many links are not seen yet. */ }; static struct links *lnktop=nil, **lnk= nil; struct links *new; if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) { lnk= &lnktop; while (*lnk != nil && !((*lnk)->dev == st.st_dev && (*lnk)->ino == st.st_ino) ) lnk= &(*lnk)->next; if (*lnk != nil) { /* Link found, return its name. */ new= *lnk; strcpy(lnkpth, new->name); if (--new->count == 0 || strcmp(path, new->name) == 0) { /* ^^ check for cancellink. */ *lnk= new->next; free(new->name); free(new); } return lnkpth; } /* No link found, add this one. */ *lnk= new= (struct links *) malloc(sizeof(*new)); new->next= nil; new->name= (char *) malloc(strlen(path) + 1); strcpy(new->name, path); new->dev= st.st_dev; new->ino= st.st_ino; new->count= st.st_nlink - 1; } lnk= nil; return nil;}static void setstat(ino, stp) ino_t ino; struct stat *stp;/* Set backup status info, we know that backup is true. */{ struct mode md; md.md_mode = stp->st_mode; md.md_uid = stp->st_uid; md.md_gid = stp->st_gid; md.md_rdev = stp->st_rdev; md.md_devsiz = stp->st_size / 1024; if (lseek(filemodes, (off_t) sizeof(md) * (off_t) ino, 0) == -1 || write(filemodes, (char *) &md, sizeof(md)) != sizeof(md) ) perrx(BACKUP);}static int getstat(name, stp) char *name; struct stat *stp;/* Get status information of file name, skipping some files. Backup info * is inserted as needed. */{ errno= 0; if (strcmp(name, BACKUP) == 0) return -1; if (lstat(name, stp) < 0) return -1; if (stp->st_mode == S_IFREG && stp->st_mtime == 0) return -1; if (backup) { struct mode md; if (lseek(filemodes, (off_t) sizeof(md) * (off_t) stp->st_ino, 0) == -1 || read(filemodes, (char *) &md, sizeof(md)) != sizeof(md) || md.md_mode == 0 ) { errno= 0; setstat(stp->st_ino, stp); } else { stp->st_mode = md.md_mode; stp->st_uid = md.md_uid; stp->st_gid = md.md_gid; stp->st_rdev = md.md_rdev; if (S_ISBLK(stp->st_mode)) stp->st_size= (off_t) md.md_devsiz * 1024; } } return 0;}static int advance()/* Determine next pathname, return true on success. */{ for (;;) { if (E==nil) { /* First call, enter root dir. */ E= (struct entry *) malloc(sizeof(*E)); E->dir= nil; E->con= nil; E->next= nil; E->name= (char *) malloc(3); strcpy(E->name, transparent ? ".@" : "."); } else if (E->con != nil) /* Dir's files must be processed. */ E= E->con; else { for (;;) { /* Remove E from it's parents list, then * try next entry, if none, go to parent dir. */ struct entry *junk= E, *parent= E->dir; if (parent != nil) parent->con= E->next; E= E->next; free(junk->name); free(junk); if (E != nil) break; if ((E= parent) == nil) return 0; } } setpath(E); if (getstat(path, &st) == 0) { if (S_ISLNK(st.st_mode)) { int n; if ((n= readlink(path, lnkpth, PATH_MAX)) >= 0) { lnkpth[n]= 0; break; } } else { break; } } if (errno != 0 && errno != ENOENT) perr(path); } linkpath= islink(); DPRINTF("%s: path = %s\n", path); return 1;}static enum orders request()/* Slave reads command sent by master. */{ static char buf[64], *bp; static int n= 0; int req; for (;;) { if (n == 0) { if ((n= read(chan[0], buf, (int) sizeof(buf))) <= 0) { if (n < 0) perrx("request()"); /* Master died, try to report it then follow. */ fprintf(stderr,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -