1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
/************************************************************************
* Collection of NFS resistant exclusive creat routines *
* *
* Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands *
* Copyright (c) 1999-2001, Philip Guenther, The United States *
* of America *
* #include "../README" *
************************************************************************/
#ifdef RCS
static /*const*/char rcsid[]=
"$Id: exopen.c,v 1.44 2001/08/26 21:10:11 guenther Exp $";
#endif
#include "procmail.h"
#include "acommon.h"
#include "robust.h"
#include "misc.h"
#include "exopen.h"
#include "lastdirsep.h"
#include "sublib.h"
static const char*safehost P((void)) /* return a hostname safe for filenames */
{ static const char*sname=0;
if(!sname)
{ char*to;const char*from=hostname();
int badchars=0;
for(to=(char*)from;*to;to++) /* check for characters that shouldn't */
if(*to=='/'||*to==':'||*to=='\\') /* be in hostnames */
badchars++;
if(!badchars) /* it's clean, pass it through */
sname=from;
else if(!(sname=to=malloc(3*badchars+strlen(from)+1)))
sname=""; /* no memory! */
else
{ int c;
while(badchars)
switch(c=(unsigned char)*from++)
{ default:*to++=c;
break;
case '\0':from--;to--; /* "this cannot happen" */
break;
case '/':case ':':case '\\': /* we'll remap them to \ooo */
*to++='\\';
*to++='0'+(c>>6);
*to++='0'+((c>>3)&7);
*to++='0'+(c&7);
badchars--;
}
strcpy(to,from); /* copy the remaining characters */
}
}
return sname;
}
int unique(full,p,len,mode,verbos,flags)char*const full;char*p;
const size_t len;const mode_t mode;const int verbos,flags;
{ static const char s2c[]=".,+%";static int serial=STRLEN(s2c);
static time_t t;char*dot,*end,*host;struct stat filebuf;
int nicediff,i,didnice,retry=RETRYunique;
if(flags&doCHOWN) /* semi-critical, try raising the priority */
{ nicediff=nice(0);SETerrno(0);nicediff-=nice(-NICE_RANGE);
didnice=!errno; /* did we succeed? */
}
end=full+len;
if(end-p<=UNIQnamelen-1) /* were we given enough space? */
goto ret0; /* nope, give up */
if(flags&doMAILDIR) /* 'official' maildir format */
dot=p;
else /* 'traditional' format */
*p=UNIQ_PREFIX,dot=ultoan((unsigned long)thepid,p+1);
if(serial<STRLEN(s2c))
goto in;
do
{ if(serial>STRLEN(s2c)-1) /* roll over the count? */
{ ;{ time_t t2;
while(t==(t2=time((time_t*)0))) /* make sure time has passed */
ssleep(1); /* tap tap tap... */
serial=0;t=t2;
}
in: if(flags&doMAILDIR)
{ dot=ultstr(0,(unsigned long)t,p); /* time.pid_s.hostname */
*dot='.';
dot=ultstr(0,(unsigned long)thepid,dot+1);
*dot++='_';
host=dot+2;
}
else
host=1+ultoan((unsigned long)t,dot+1); /* _pid%time.hostname */
host[-1]='.'; /* add the ".hostname" part */
strlcpy(host,safehost(),end-host);
}
*dot=(flags&doMAILDIR)?'0'+serial:s2c[serial];
serial++;
i=lstat(full,&filebuf);
#ifdef ENAMETOOLONG
if(i&&errno==ENAMETOOLONG)
{ char*op,*ldp;
op=lastdirsep(full);
ldp=op+1; /* keep track to avoid shortening past it */
if(end-op>MINnamelen+1) /* guess at a safe length */
op+=MINnamelen+1; /* start at MINnamelen out */
else
op=end-1; /* this shouldn't happen, but... */
do
*--op='\0'; /* try chopping */
while((i=lstat(full,&filebuf))&&errno==ENAMETOOLONG&&op>ldp);
} /* either it stopped being a problem or we ran out of filename */
#endif
}
#ifndef O_CREAT
#define ropen(path,type,mode) creat(path,mode)
#endif
while((!i||errno!=ENOENT|| /* casually check if it already exists */
(0>(i=ropen(full,O_WRONLY|O_CREAT|O_EXCL,mode))&&errno==EEXIST))&&
(i= -1,retry--));
if(flags&doCHOWN&&didnice)
nice(nicediff); /* put back the priority to the old level */
if(i<0)
{ if(verbos) /* this error message can be confusing */
writeerr(full); /* for casual users */
goto ret0;
}
#ifdef NOfstat
if(flags&doCHOWN)
{ if(
#else
if(flags&doCHECK)
{ struct stat fdbuf;
fstat(i,&fdbuf); /* match between the file descriptor */
#define NEQ(what) (fdbuf.what!=filebuf.what) /* and the file? */
if(lstat(full,&filebuf)||filebuf.st_nlink!=1||filebuf.st_size||
NEQ(st_dev)||NEQ(st_ino)||NEQ(st_uid)||NEQ(st_gid)||
flags&doCHOWN&&
#endif
chown(full,uid,sgid))
{ rclose(i);unlink(full); /* forget it, no permission */
ret0: return flags&doFD?-1:0;
}
}
if(flags&doLOCK)
rwrite(i,"0",1); /* pid 0, `works' across networks */
if(flags&doFD)
return i;
rclose(i);
return 1;
}
/* rename MUST fail if already existent */
int myrename(old,newn)const char*const old,*const newn;
{ int fd,serrno;
fd=hlink(old,newn);serrno=errno;unlink(old);
if(fd>0)rclose(fd-1);
SETerrno(serrno);
return fd<0?-1:0;
}
/* NFS-resistant link() */
int rlink(old,newn,st)const char*const old,*const newn;struct stat*st;
{ if(link(old,newn))
{ register int serrno,ret;struct stat sto,stn;
serrno=errno;ret= -1;
#undef NEQ /* compare files to see if the link() */
#define NEQ(what) (sto.what!=stn.what) /* actually succeeded */
if(lstat(old,&sto)||(ret=1,lstat(newn,&stn)||
NEQ(st_dev)||NEQ(st_ino)||NEQ(st_uid)||NEQ(st_gid)||
S_ISLNK(sto.st_mode))) /* symlinks are also bad */
{ SETerrno(serrno);
if(st&&ret>0)
{ *st=sto; /* save the stat data */
return ret; /* it was a real failure */
}
return -1;
}
/*SETerrno(serrno);*/ /* we really succeeded, don't bother with errno */
}
return 0;
}
/* hardlink with fallback for systems that don't support it */
int hlink(old,newn)const char*const old,*const newn;
{ int ret;struct stat stbuf;
if(0<(ret=rlink(old,newn,&stbuf))) /* try a real hardlink */
{ int fd;
#ifdef O_CREAT /* failure due to filesystem? */
if(stbuf.st_nlink<2&&errno==EXDEV&& /* try it by conventional means */
0<=(fd=ropen(newn,O_WRONLY|O_CREAT|O_EXCL,stbuf.st_mode)))
return fd+1;
#endif
return -1;
}
return ret; /* success, or the stat failed also */
}
|