/*
SymHarden -- replace symlinked file by hardlink
Copyright (C) 2009 Andreas Neuper
SymHarden is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
SymHarden is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SymHarden. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#define VERSION "1.0.2"
/**********************VERSION HISTORY**********************
1.0.1 initial version
1.0.2 test for existence of temp-file
***********************************************************/
usage(char*v)
{
char*a=strrchr(v,'/');
if(a!=NULL)a++;
else a=v;
fprintf(stderr,
"%s Version " VERSION "\n\nSYNTAX:\t%s \n"
"\treplace symlink by hardlink if possible\n"
#if 0
"\treturn\t0 on success,\n"
"\t\t1 on directories or plain files,\n"
"\t\t2 on looped links,\n"
"\t\t3 on nonexistent path or reference,\n"
"\t\t4 on missing permissions,\n"
"\t\t-1 on missing target\n"
#endif
"\n",v,a);
exit(-1);
}
char*temp=NULL; /* must be global to allow atexit(restore) */
void restore(void)
{
char*a=strdup(temp);
a[strlen(a)-10]=0;
rename(temp,a);
/* might complain if executed in vain */
/* added for security reasons only */
free(temp);
}
char*mkmytemp(const char*v)
{
struct stat a;
do {
temp=malloc(strlen(v)+10);
strcpy(temp,v);
strcat(temp,".s2hXXXXXX");
mktemp(temp);
} while(!lstat(temp,&a));
atexit(restore);
}
char*linkto(const char*path)
{
char*buf=NULL;
size_t lgth=1024,lsz,ret;
do {
lgth<<=1;
if(buf==NULL){free(buf);}
buf=malloc(lgth);
lsz=readlink(path,buf,lgth);
}while( (lgth==lsz) );
/* if buffer too short errno==ENAMETOOLONG condition would not fail! */
if(errno!=0)
{
perror(path);
switch(errno)
{ case EINVAL: ret=1; break;
case ELOOP: ret=2; break;
case EACCES:
case EPERM: ret=4; break;
case ENOENT:
case ENOTDIR: ret=3; break;
default: ret=-1;
/* for some reason these error codes are not correct in most cases */
}
exit(ret);
} else
buf[lsz]=0; /* is not terminated by default*/
return buf;
}
int
main(int n , char*v[])
{
char*target=NULL;
if(n!=2)usage(v[0]);
target=linkto(v[1]);
mkmytemp(v[1]);
rename(v[1],temp);
if(link(target,v[1]) == 0)
{
unlink(temp);
if(0) fprintf(stderr,"\nln \"%s\" \"%s\"\n",target,v[1]);
} else
{
rename(temp,v[1]);
}
exit(0);
}
/*
* Tested for
* early exit line --> atexit(restore)
* inexecutable link()
* intended temporary file name
* usage name handling
* correct behaviour of restore()
* accepted warning for mktemp (mkstemp is no alternative)
* freeing allocated space of target (not allowed to)
*/