#include "reclinker.h"
int (*preselfcall)();
void (*postselfcall)();
void (*wantmydose)(struct pathnode *root);
struct myarray *prefix;
char *prefixorig = NULL;
char *whereabs;
char *fromabs;
int forceproperprefix = 0;
#define checkUG(stru_stat) \
if ((Uid != -1 && Uid != stru_stat.st_uid) || \
(Gid != -1 && Gid != stru_stat.st_gid)) \
{ \
if (verbosity >= 2) \
printf("%s%s skipped because of non-matching uid/gid\n",fromorep,where->strmid); \
return 0; \
}
int
removelink()
{
struct stat whatstat;
struct stat anotherstat;
void removedir();
if (lstat(what, &whatstat) != 0)
error("can't stat",fromorep,where->strmid,NULL);
checkUG(whatstat);
if ((whatstat.st_mode & S_IFMT) == S_IFDIR) {
if (stat(where->str, &anotherstat) == 0) {
if ((anotherstat.st_mode & S_IFMT) == S_IFDIR)
return 1;
else {
if (verbosity >= 2) {
printf("%s%s is not a dir...\n",whereorep,where->strmid);
}
removedir();
return 0;
}
} else {
if (verbosity >= 2) {
printf("%s%s/: can't stat: %s\n",whereorep,where->strmid,strerror(errno));
}
return 0; /* We don't descend into "what" in vain */
}
}
if (firstmodeused && mode != (whatstat.st_mode & S_IRWXAUGS)) {
if (verbosity >= 2)
printf("%s%s kept because of non-matching mode\n",whereorep,where->strmid);
return 0;
}
if (lstat(where->str, &whatstat) != 0) {
if (verbosity >= 2)
printf("%s%s: can't stat: %s\n",whereorep,where->strmid,strerror(errno));
return 0;
}
if ((uid != -1 && uid != whatstat.st_uid) ||
(gid != -1 && gid != whatstat.st_gid)) {
if (verbosity >= 1)
printf("%s%s kept because of non-matching uid/gid\n",whereorep,where->strmid);
return 0;
}
if (stat(where->str, &anotherstat) != 0)
/* where->str is a broken symlink */
goto removal;
if (force) {
/* what's not a symlink is tried to be removed */
if ((whatstat.st_mode & S_IFMT) != S_IFLNK)
goto removal;
}
if (! othermodeused) { /* othermodeused used to be named onlybroken,
* it's just parsimony in option usage
*/
/* where->str is removed iff it's a symlink */
if ((whatstat.st_mode & S_IFMT) == S_IFLNK)
goto removal;
}
return 0;
removal:
if (remove(where->str) == 0) {
if (verbosity >= 1)
printf("%s%s removed\n",whereorep,where->strmid);
} else {
fprintf(stderr,"%s%s: ", whereorep,where->strmid);
perror("remove failed");
program_retval = SMALLPROB;
}
return 0;
}
void
removedir()
{
struct stat whatstat;
if (onlydir)
return;
if (lstat(where->str, &whatstat) != 0) {
if (verbosity >= 2)
printf("%s%s: can't stat: %s\n",whereorep,where->strmid,strerror(errno));
return;
}
if ((uid != -1 && uid != whatstat.st_uid) ||
(gid != -1 && gid != whatstat.st_gid)) {
return;
}
if (remove(where->str) == 0) {
if (verbosity >= 1)
printf("%s%s/ removed\n",whereorep,where->strmid);
} else if (verbosity >= 2) {
printf("%s%s/: remove failed: %s\n", whereorep,where->strmid,strerror(errno));
}
}
#define dirsuck() { \
fprintf(stderr,"%s%s/: ", whereorep,where->strmid); \
perror("dir cannot be created"); \
program_retval = SMALLPROB; \
}
/* The implementation of proceeding a file (if it's a dir, the
* counterpart is created on the other side, else it's symlinked to the
* other side)
*/
int
linker()
{
struct stat whatstat;
/* char *to_linkto; */
int cdrv = 0;
if (lstat(what, &whatstat) != 0)
error("can't stat",fromorep,where->strmid,NULL);
checkUG(whatstat);
if (! firstmodeused)
mode = (whatstat.st_mode & S_IRWXAUGS);
if ((whatstat.st_mode & S_IFMT) == S_IFDIR) {
cdrv = createdir();
if (cdrv == -2) {
if (!force) {
dirsuck();
return 0;
}
}
else
return 1;
}
if (force) {
if (remove(where->str) != 0 && errno != ENOENT) {
/* Maybe this block should be replaced with the invocation of a more
* general error fuction than the actual one ??
*/
program_retval = SMALLPROB;
fprintf(stderr,"%s%s: ",whereorep,where->strmid);
perror("forcing creation failed");
}
if (cdrv == -2) {
if (createdir() == -2) {
dirsuck();
return 0;
}
else
return 1;
}
}
if (onlydir) return 0;
if (symlink(prefix->str,where->str) == 0) {
if (verbosity >= 1) {
printf("%s%s -> %s",whereorep,where->strmid,prefix->str);
if (verbosity >= 2)
printf(": symlink created");
putchar('\n');
}
chowner();
} else {
program_retval = SMALLPROB;
fprintf(stderr,"%s%s: ",whereorep,where->strmid);
perror("the symlink cannot be created");
}
return 0;
}
void
modeforcer()
{
if (! othermodeused)
return;
if (chmod(where->str,mode_forced) == 0) {
if (verbosity >= 2)
printf("%s%s/: mode set to %o\n", whereorep,
where->strmid, (int)mode_forced);
} else {
program_retval = SMALLPROB;
fprintf(stderr,"%s%s/: ",whereorep,where->strmid);
perror("setting mode failed");
}
}
int
test()
{
struct stat whatstat;
struct stat wherestat;
int retval;
char *typeind;
int stat_retval;
/* First we stat the actually processed entity, what.
It's *very* strange if this fails, so then we go error */
if (lstat(what, &whatstat) != 0)
error("can't stat",fromorep,where->strmid,NULL);
checkUG(whatstat);
if ((whatstat.st_mode & S_IFMT) == S_IFDIR) {
/* Here we'll stat where->str;
if what was a dir, then we need it resolved
otherwise as is */
retval = 1;
typeind = "/";
stat_retval = stat(where->str,&wherestat);
} else {
retval = 0;
typeind = "";
stat_retval = lstat(where->str,&wherestat);
}
if (stat_retval != 0) {
printf("%s%s%s: can't stat: %s\n",whereorep,where->strmid,typeind,strerror(errno));
program_retval = SMALLPROB;
return 0; /* We don't descend into what in vain */
} else if ((firstmodeused && mode != (whatstat.st_mode & S_IRWXAUGS)) ||
(uid != -1 && uid != wherestat.st_uid) ||
(gid != -1 && gid != wherestat.st_gid)) {
if (retval == 0) {
printf("%s%s%s: permissions, owner or group is not as it's required\n",whereorep,where->strmid,typeind);
program_retval = SMALLPROB;
}
return retval;
}
if (retval) {
/* "what" is a dir */
if ((wherestat.st_mode & S_IFMT) == S_IFDIR) {
if (verbosity >= 2)
printf("%s%s/ is OK\n",whereorep,where->strmid);
} else {
printf("%s%s/ exists but it's not a dir\n",whereorep,where->strmid);
program_retval = SMALLPROB;
retval = 0; /* don't descend into "what" in vain */
}
}
else if ((wherestat.st_mode & S_IFMT) != S_IFLNK) {
program_retval = SMALLPROB;
printf("%s%s exists but it's not a symlink\n",whereorep,where->strmid);
return retval;
} else if (! rec_pointto(where->str,what)) {
program_retval = SMALLPROB;
printf("%s%s is an existing symlink but it's not resolved in %s%s\n",whereorep,where->strmid,fromorep,where->strmid);
return retval;
} else {
char *l, *f;
int v;
l = canon_readlink(where->str);
f = soft_realpath(what);
v = strcmp(l,f);
free(l);
free(f);
if (v != 0) {
program_retval = SMALLPROB;
printf("%s%s is a symlink resolved in %s%s, but it's pointing there only indirectly\n",whereorep,where->strmid,fromorep,where->strmid);
return retval;
} else {
f = my_readlink(where->str);
v = strcmp(f,prefix->str);
free(f);
if (v != 0) {
program_retval = SMALLPROB;
printf("%s%s is a symlink pointing to where it should but the value is not named as it should be\n",whereorep,where->strmid);
} else if (verbosity >= 2) {
printf("%s%s is OK\n",whereorep,where->strmid);
}
}
}
return retval;
}
void
noop()
{
}
/* The following two functions can serve as "wantmydose" */
void
fetchdir(struct pathnode *root)
{
DIR *dir;
struct dirent *entry;
struct pathnode *pn;
dir = opendir(".");
pn = root;
while ((entry = readdir(dir))) {
addnode(pn,entry->d_name);
}
if (closedir(dir) != 0)
error((char *)GETCWD(NULL,0),"unable to close dir");
}
void
nextinpath(struct pathnode *root)
{
if (indivfile->next != NULL) {
indivfile = indivfile->next;
addnode(root,indivfile->name);
}
}
#define backabit(arr,n) \
{ \
arr->strend -= n; \
*(arr->strend) = '\0'; \
}
#define forthabit(arr,n) arr->str += n
struct pathstate {
struct myarray *prefix;
struct pathnode *root;
struct pathnode *pn;
int postgrowth;
};
/*
* A recursive function to walk throgh all the source dir tree and
* calling linker on the certain files
*/
void
reclinker()
{
/*
* State information which is kept around over recursive calls
* is quarantined in pstate
*/
struct pathstate pstate;
memset(&pstate, 0, sizeof(pstate));
initpath(pstate.root);
wantmydose(pstate.root);
pstate.pn = pstate.root;
while ((pstate.pn = pstate.pn->next)) {
int prune = 0;
char *aux = NULL, *realwhere = NULL;
what = pstate.pn->name;
if (strcmp(what,".") == 0 || strcmp(what,"..") == 0)
continue;
pstate.postgrowth = strlen(what) + 1;
appendasadir(where,what);
if (deletemode != 1)
appendasadir(prefix,what);
/*
* We don't descend into directories which reside inside
* the mirrored tree
*/
realwhere = my_realpath(where->str);
if (realwhere)
aux = strreduce(realwhere, fromabs);
if (aux != NULL && (*aux == '\0' || *aux == '/')) {
struct stat astat;
stat(realwhere, &astat);
if ((astat.st_mode & S_IFMT) == S_IFDIR) {
prune = 1;
if (verbosity >= 2)
printf("pruning %s%s as it's contained in original copy\n", whereorep, where->strmid);
}
}
free(aux);
if (! prune && preselfcall()) {
/* preselfcall is a function pointer, may point to
* linker, removelink or test */
if (chdir(what) == 0) {
if (rel) {
struct stat whatstat;
lstat(where->str,&whatstat);
if (forceproperprefix || (whatstat.st_mode & S_IFMT) == S_IFLNK) {
char *abspref, *pathbetween;
abspref = (char *)MALLOC(strlen(whereabs) + strlen(prefixorig) + strlen(where->strmid) + 2);
strcpy(abspref,whereabs);
strcat(abspref,"/");
strcat(abspref,prefixorig);
strcat(abspref,where->strmid);
if (!forceproperprefix)
dupemyarray(prefix, pstate.prefix);
resetmyarray(prefix);
if (! realwhere)
/*
* where->str might be ceated in preselfcall()
* in which case realwhere needs to be recomputed
*/
realwhere = my_realpath(where->str);
/* XXX this assertion doesn't cares about races with the real world */
assert(realwhere);
pathbetween = str_relpath(realwhere,abspref);
appendtomyarray(prefix,pathbetween);
free(abspref);
free(pathbetween);
}
else
prependtomyarray(prefix,"../");
}
free(realwhere);
reclinker();
if (chdir("..") == 0) {
if (rel) {
if (pstate.prefix == NULL) {
forthabit(prefix,3);
} else {
freemyarray(prefix);
prefix = pstate.prefix;
}
}
/* If we want to set the mode of
* created dirs arbitrarily, we have to
* do it here, after recursive calls of
* reclinker() are over, thus avioding
* potential read/write conflicts of
* children reclinker()'s
*/
postselfcall();
/* It's a function pointer which may
* point to modeforcer, removedir or
* noop
*/
} else
error("can't go back to parent dir",what,NULL);
/*Unlikely error*/
} else {
free(realwhere);
program_retval = SMALLPROB;
fprintf(stderr,"%s%s: ",fromorep,where->strmid);
perror("unable to enter dir");
}
} else
free(realwhere);
backabit(where, pstate.postgrowth);
if (deletemode != 1)
backabit(prefix, pstate.postgrowth);
}
freepath(pstate.root);
}
int
main(int argc, char **argv)
{
int o;
char *aux;
int auxlength;
void act_as_reclinker(int argc, char** argv);
void act_as_recdeleter(int argc, char** argv);
void act_as_reclinktester(int argc, char** argv);
int len;
me = (char*)basename(argv[0]);
defaults();
if (strcmp(me,DELETE_INVOKE) == 0) deletemode=1;
if (strcmp(me,TEST_INVOKE) == 0) deletemode=2;
while ((o = getopt (argc, argv, "hvqdltrfDm:ou:g:U:G:p:")) != -1) {
switch (o) {
case 'h':
usage(stdout);
showhelp();
exit(BADOPT);
case 'v':
verbosity += 1;
break;
case 'q':
verbosity -= 1;
break;
case 'r':
rel=1;
break;
case 'd':
deletemode=1;
break;
case 'l':
deletemode=0;
break;
case 't':
deletemode=2;
break;
case 'f':
force=1;
break;
case 'D':
onlydir=1;
break;
case 'm':
mode = parsemode(optarg);
firstmodeused = 1;
break;
case 'o':
othermodeused = 1;
break;
case 'u':
usrname = optarg;
if ((uid = parseuid(optarg)) == -1) {
fprintf(stderr, "%s: no such user\n", usrname);
exit(FATAL);
}
break;
case 'g':
grpname = optarg;
if ((gid = parsegid(optarg)) == -1) {
fprintf(stderr, "%s: no such group\n", grpname);
exit(FATAL);
}
break;
case 'U':
Usrname = optarg;
if ((Uid = parseuid(optarg)) == -1) {
fprintf(stderr, "%s: no such user\n", Usrname);
exit(FATAL);
}
break;
case 'G':
Grpname = optarg;
if ((Gid = parsegid(optarg)) == -1) {
fprintf(stderr, "%s: no such group\n", Grpname);
exit(FATAL);
}
break;
case 'p':
prefixorig = optarg;
break;
case '?':
badopt;
}
}
if (argc < optind + 2) {
badopt;
}
aux = (char*) GETCWD(NULL,0);
auxlength = strlen(aux) + 1;
aux = (char *)REALLOC(aux,auxlength);
strcat(aux,"/");
#define makerepstr(wg,wp) { \
len = strlen(wg); \
len--; \
if (*(wg + len) == '/') { \
wp = (char *)malloc(len + 1); \
memcpy(wp,wg,len); \
*(wp + len) = '\0'; \
} else { \
wp = wg; \
} \
}
whereorig = argv[optind+1];
makerepstr(whereorig,whereorep);
initmyarray(where);
if (*whereorig == '/') {
appendtomyarray(where,normalize(whereorig));
} else {
aux = (char *)REALLOC(aux,auxlength + strlen(whereorig) + 1);
appendtomyarray(where,normalize(strcat(aux,whereorig)));
}
where->strmid = where->strend;
fromorig = argv[optind];
makerepstr(fromorig,fromorep);
if (*fromorig == '/') {
from = normalize(fromorig);
} else {
aux = (char *)REALLOC(aux,auxlength + strlen(fromorig) + 1);
*(aux + auxlength) = '\0';
from = normalize(strcat(aux,fromorig));
}
free(aux);
fromabs = my_realpath(from);
switch (deletemode) {
case 0:
act_as_reclinker(argc,argv);
break;
case 1:
act_as_recdeleter(argc,argv);
break;
case 2:
act_as_reclinktester(argc,argv);
break;
default:
fprintf(stderr,"%d: no such mode defined\n",deletemode);
exit(BADOPT);
}
if (argc >= optind + 3) {
int i;
wantmydose = nextinpath;
for (i=optind+2;i < argc;i++) {
if (chdir(from) != 0) {
fprintf(stderr,"%s/: ",from);
perror("can't enter source directory");
exit(FATAL);
}
if (strcmp(argv[i],"-") != 0 &&
strcmp(argv[i],"0-") != 0) {
indivfile = strtopath(argv[i]);
reclinker();
postselfcall();
} else {
char line[PATH_MAX], sepchar;
int res;
unsigned lno;
switch((*argv[i])) {
case '-':
sepchar = '\n';
break;
case '0':
sepchar = '\0';
break;
default:
fprintf(stderr, "stdin parsing mode sigill is weird\n");
abort();
}
for (lno = 1; lno++; ) {
res = get_line_from_file(stdin, line, sizeof(line), sepchar);
if (res <= 0) {
if (res == 0)
break;
fprintf(stderr, "invalid input line %u\n", lno);
exit(FATAL);
}
indivfile = strtopath(line);
reclinker();
postselfcall();
}
}
}
} else {
wantmydose = fetchdir;
reclinker();
postselfcall();
}
exit(program_retval);
}
#define initprefix { \
if (prefixorig == NULL) { \
if (rel) { \
prefixorig = str_relpath(whereabs,from); \
} else \
prefixorig = from; \
} \
initmyarray(prefix); \
appendtomyarray(prefix,prefixorig); \
if (!strsubtest(prefix->str,"../")) \
forceproperprefix = 1; \
}
void
act_as_reclinker(int argc, char** argv)
{
char* currdir;
char* aux = NULL;
int isnew;
if (firstmodeused) {
mode_forced = mode;
mode = (mode| S_IWUSR | S_IXUSR);
if (mode != mode_forced && (! othermodeused)) {
fprintf(stderr,
"Prescribed mode given by -m is supported only for modes including\n"
"write/execute permissions for user -- if you *really* want another mode,\n"
"use -o as well\n");
exit(BADOPT);
}
} else { struct stat fromstat;
if (stat(from,&fromstat) != 0) {
error("can't stat",fromorig,NULL);
}
mode = fromstat.st_mode & S_IRWXAUGS;
}
/* The createdir() fucntion uses the value of mask (for reoporting only) */
mask = umask (0);
umask (mask);
isnew = createdir();
if (isnew == -2) {
/* Bah. I couldn't help putting here this block which is taken
* from linker() with small modifications. Ugly solution,
* anyway.
*/
if (force) {
if (remove(where->str) != 0 && errno != ENOENT) {
/* Maybe this block should be replaced with the invocation of a more
* general error fuction than the actual one ??
*/
program_retval = SMALLPROB;
fprintf(stderr,"%s%s: ",whereorep,where->strmid);
perror("forcing creation failed");
} else
isnew = createdir();
if (isnew != -2)
goto goon;
}
fprintf(stderr, "creating target directory failed\n");
exit(FATAL);
}
goon:
if (chdir(from) != 0) {
fprintf(stderr,"%s: ",fromorig);
perror("can't enter source directory");
cleanup_aux(isnew);
exit(FATAL);
}
/* The following block is for avoiding infite loops and overwriting
* files in source dir with symlinks
*/
whereabs = my_realpath(where->str);
currdir = (char*) GETCWD(NULL,0);
if (whereabs)
aux = strreduce(whereabs, currdir);
if (aux != NULL && (*aux == '\0' || *aux == '/')) {
/* Eh, and if from is subdir of where, ain't that a problem?? */
fprintf(stderr,"Target dir cannot be subdir of source dir\n");
/* Problem: "whereorig" is created regardless the above anomaly shows
* up; quick'n'dirty solution: if it's newly created, we remove it
* immediately
*/
cleanup_aux(isnew);
exit(BADOPT);
}
free(currdir);
free(aux);
initprefix;
preselfcall = linker;
postselfcall = modeforcer;
}
void
act_as_recdeleter(int argc, char** argv)
{
if (chdir(from) != 0) {
fprintf(stderr,"%s/: ",from);
perror("can't enter source directory");
exit(FATAL);
}
/* what = from; */
preselfcall = removelink;
postselfcall = removedir;
what = my_realpath(from);
if (preselfcall() == 0)
exit(SUCCESS);
free(what);
rel=0; /*recdeleter makes no use of -r*/
}
void
act_as_reclinktester(int argc, char** argv)
{
/* struct stat wherestat; */
if (chdir(from) != 0) {
fprintf(stderr,"%s/: ",from);
perror("can't enter source directory");
exit(FATAL);
}
whereabs = my_realpath(where->str);
initprefix;
preselfcall = test;
postselfcall = noop;
what = my_realpath(from);
if (preselfcall() == 0)
exit(SUCCESS);
free(what);
}
syntax highlighted by Code2HTML, v. 0.9.1