1/************************************************************************ 2 * Whatever is needed for (un)locking files in various ways * 3 * * 4 * Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands * 5 * Copyright (c) 1998-2001, Philip Guenther, The United States * 6 * of America * 7 * #include "../README" * 8 ************************************************************************/ 9#ifdef RCS 10static /*const*/char rcsid[]= 11 "$Id: locking.c,v 1.63 2001/08/04 07:12:17 guenther Exp $"; 12#endif 13#include "procmail.h" 14#include "robust.h" 15#include "shell.h" 16#include "misc.h" 17#include "pipes.h" 18#include "foldinfo.h" 19#include "exopen.h" 20#include "locking.h" 21#include "lastdirsep.h" 22 23char*globlock; 24 25int lockit(name,lockp)char*name;char**const lockp; 26{ int permanent=nfsTRY,triedforce=0,locktype=doLOCK;struct stat stbuf;time_t t; 27 zombiecollect(); 28 if(*lockp) 29 { if(!strcmp(name,*lockp)) /* compare the previous lockfile to this one */ 30 { free(name);return 1; /* they're equal, save yourself some effort */ 31 } 32 unlock(lockp); /* unlock any previous lockfile FIRST */ 33 } /* to prevent deadlocks (I hate deadlocks) */ 34 if(!*name) 35 { free(name);return 1; 36 } 37 if(!strcmp(name,defdeflock)) /* is it the system mailbox lockfile? */ 38 { locktype=doCHECK|doLOCK; 39 if(sgid!=gid&&setegid(sgid)) /* try and get some extra permissions */ 40#ifndef fdlock 41 if(!accspooldir) 42 { yell("Bypassed locking",name); 43 free(name);return 0; 44 } 45#endif 46 ; 47 } 48 for(lcking|=lck_DELAYSIG;;) 49 { yell("Locking",name); /* in order to cater for clock skew: get */ 50 if(!xcreat(name,LOCKperm,&t,locktype)) /* time t from the filesystem */ 51 { *lockp=name; /* lock acquired, hurray! */ 52 break; 53 } 54 switch(errno) 55 { case EEXIST: /* check if it's time for a lock override */ 56 if(!lstat(name,&stbuf)&&stbuf.st_size<=MAX_locksize&&locktimeout 57 &&!lstat(name,&stbuf)&&locktimeout<t-stbuf.st_mtime) 58 /* 59 * stat() till unlink() should be atomic, but can't guarantee that 60 */ 61 { if(triedforce) /* already tried, not trying */ 62 goto faillock; /* again */ 63 if(S_ISDIR(stbuf.st_mode)||unlink(name)) 64 triedforce=1,nlog("Forced unlock denied on"),logqnl(name); 65 else 66 { nlog("Forcing lock on");logqnl(name);suspend(); 67 goto ce; 68 } 69 } 70 else 71 triedforce=0; /* legitimate iteration, clear flag */ 72 break; 73 case ENOSPC: /* no space left, treat it as a transient */ 74#ifdef EDQUOT /* NFS failure */ 75 case EDQUOT: /* maybe it was a short term shortage? */ 76#endif 77 case ENOENT:case ENOTDIR:case EIO:/*case EACCES:*/ 78 if(--permanent) 79 goto ds; 80 goto faillock; 81#ifdef ENAMETOOLONG 82 case ENAMETOOLONG: /* maybe filename too long, shorten and retry */ 83 { int i; 84 if(0<(i=strlen(name)-1)&&!strchr(dirsep,name[i-1])) 85 { nlog("Truncating");logqnl(name);elog(" and retrying lock\n"); 86 name[i]='\0';permanent=nfsTRY; 87 goto ce; 88 } 89 } 90#endif 91 default: 92faillock: nlog("Lock failure on");logqnl(name); 93 goto term; 94 } 95 permanent=nfsTRY; 96ds: ssleep((unsigned)locksleep); 97ce: if(nextexit) 98term: { free(name); /* drop the preallocated buffer */ 99 break; 100 } 101 } 102 if(!privileged) /* we already set our ids */ 103 setegid(gid); /* we put back our regular permissions */ 104 lcking&=~lck_DELAYSIG; 105 if(nextexit) 106 elog(whilstwfor),elog("lockfile"),logqnl(name),Terminate(); 107 return !!*lockp; 108} 109 110int lcllock(noext,withext) /* lock a local lockfile */ 111 const char*const noext,*const withext; 112{ char*lckfile; /* locking /dev/null or | would be silly */ 113 if(noext||(strcmp(withext,devnull)&&strcmp(withext,"|"))) 114 { if(noext) 115 lckfile=tstrdup(noext); 116 else 117 { size_t len=strlen(withext); 118 lckfile=malloc(len+strlen(lockext)+1); 119 strcpy(strcpy(lckfile,withext)+len,lockext); 120 } 121 if(globlock&&!strcmp(lckfile,globlock)) /* same as global lockfile? */ 122 { nlog("Deadlock attempted on");logqnl(lckfile); 123 free(lckfile); 124 return 0; 125 } 126 else 127 return lockit(lckfile,&loclock); 128 } 129 return 1; 130} 131 132void unlock(lockp)char**const lockp; 133{ onguard(); 134 if(*lockp) 135 { if(!strcmp(*lockp,defdeflock)) /* is it the system mailbox lockfile? */ 136 setegid(sgid); /* try and get some extra permissions */ 137 yell("Unlocking",*lockp); 138 if(unlink(*lockp)) 139 nlog("Couldn't unlock"),logqnl(*lockp); 140 if(!privileged) /* we already set our ids */ 141 setegid(gid); /* we put back our regular permissions */ 142 if(!nextexit) /* if not inside a signal handler */ 143 free(*lockp); 144 *lockp=0; 145 } 146 offguard(); 147} 148 /* an NFS secure exclusive file open */ 149int xcreat(name,mode,tim,chownit)const char*const name;const mode_t mode; 150 time_t*const tim;const int chownit; 151{ char*p;int j= -2;size_t i; 152 i=lastdirsep(name)-name; 153 memcpy(p=malloc(i+UNIQnamelen),name,i); /* try & rename */ 154 if(unique(p,p+i,i+UNIQnamelen,mode,verbose,chownit)) /* a unique filename */ 155 { if(tim) 156 { struct stat stbuf; /* return the filesystem time to the caller */ 157 stat(p,&stbuf);*tim=stbuf.st_mtime; 158 } 159 j=myrename(p,name); 160 } 161 free(p); 162 return j; 163} 164 /* if you've ever wondered what conditional compilation was good for */ 165#ifndef fdlock /* watch closely :-) */ 166#ifdef USEflock 167#ifndef SYS_FILE_H_MISSING 168#include <sys/file.h> 169#endif 170#define REITflock 1 171#else 172#define REITflock 0 173#endif /* USEflock */ 174static int oldfdlock= -1; /* the fd we locked last */ 175#ifndef NOfcntl_lock 176static struct flock flck; /* why can't it be a local variable? */ 177#define REITfcntl 1 178#else 179#define REITfcntl 0 180#endif /* NOfcntl_lock */ 181#ifdef USElockf 182static off_t oldlockoffset; 183#define REITlockf 1 184#else 185#define REITlockf 0 186#endif /* USElockf */ 187 188int fdlock(fd)int fd; 189{ int ret; 190 if(verbose) 191 nlog("Acquiring kernel-lock\n"); 192#if REITfcntl+REITflock+REITlockf>1 193 for(;!toutflag;verbose&&(nlog("Reiterating kernel-lock\n"),0), 194 ssleep((unsigned)locksleep)) 195#endif 196 { zombiecollect(); 197#ifdef USElockf 198 oldlockoffset=tell(fd); 199#endif 200#ifndef NOfcntl_lock 201 flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_len=0; 202#ifdef USElockf 203 flck.l_start=oldlockoffset; 204#else 205 flck.l_start=tell(fd); 206#endif 207#endif 208 lcking|=lck_KERNEL; 209#ifndef NOfcntl_lock 210 ret=fcntl(fd,F_SETLKW,&flck); 211#ifdef USElockf 212 if((ret|=lockf(fd,F_TLOCK,(off_t)0))&&(errno==EAGAIN||errno==EACCES|| 213 errno==EWOULDBLOCK)) 214ufcntl: 215 { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck); 216 continue; 217 } 218#ifdef USEflock 219 if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| 220 errno==EWOULDBLOCK)) 221 { lockf(fd,F_ULOCK,(off_t)0); 222 goto ufcntl; 223 } 224#endif /* USEflock */ 225#else /* USElockf */ 226#ifdef USEflock 227 if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| 228 errno==EWOULDBLOCK)) 229 { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck); 230 continue; 231 } 232#endif /* USEflock */ 233#endif /* USElockf */ 234#else /* NOfcntl_lock */ 235#ifdef USElockf 236 ret=lockf(fd,F_LOCK,(off_t)0); 237#ifdef USEflock 238 if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| 239 errno==EWOULDBLOCK)) 240 { lockf(fd,F_ULOCK,(off_t)0); 241 continue; 242 } 243#endif /* USEflock */ 244#else /* USElockf */ 245#ifdef USEflock 246 ret=flock(fd,LOCK_EX); 247#endif /* USEflock */ 248#endif /* USElockf */ 249#endif /* NOfcntl_lock */ 250 oldfdlock=fd;lcking&=~lck_KERNEL; 251 return ret; 252 } 253 return 1; /* timed out */ 254} 255 256int fdunlock P((void)) 257{ int i; 258 if(oldfdlock<0) 259 return -1; 260 i=0; 261#ifdef USEflock 262 i|=flock(oldfdlock,LOCK_UN); 263#endif 264#ifdef USElockf 265 ;{ off_t curp=tell(oldfdlock); /* restore the position later */ 266 lseek(oldfdlock,oldlockoffset,SEEK_SET); 267 i|=lockf(oldfdlock,F_ULOCK,(off_t)0);lseek(oldfdlock,curp,SEEK_SET); 268 } 269#endif 270#ifndef NOfcntl_lock 271 flck.l_type=F_UNLCK;i|=fcntl(oldfdlock,F_SETLK,&flck); 272#endif 273 oldfdlock= -1; 274 return i; 275} 276#endif /* fdlock */ 277