]> git.decadent.org.uk Git - dak.git/blob - tools/dsync-0.0/libdsync/contrib/fileutl.cc
dinstall
[dak.git] / tools / dsync-0.0 / libdsync / contrib / fileutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: fileutl.cc,v 1.5 1999/10/24 06:53:12 jgg Exp $
4 /* ######################################################################
5    
6    File Utilities
7    
8    CopyFile - Buffered copy of a single file
9    GetLock - dpkg compatible lock file manipulation (fcntl)
10    
11    This source is placed in the Public Domain, do with it what you will
12    It was originally written by Jason Gunthorpe.
13    
14    ##################################################################### */
15                                                                         /*}}}*/
16 // Include Files                                                        /*{{{*/
17 #ifdef __GNUG__
18 #pragma implementation "dsync/fileutl.h"
19 #endif 
20 #include <dsync/fileutl.h>
21 #include <dsync/error.h>
22
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/fcntl.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <signal.h>
29 #include <wait.h>
30 #include <errno.h>
31 #include <iostream>
32
33 using namespace std;
34                                                                         /*}}}*/
35
36 // CopyFile - Buffered copy of a file                                   /*{{{*/
37 // ---------------------------------------------------------------------
38 /* The caller is expected to set things so that failure causes erasure */
39 bool CopyFile(FileFd &From,FileFd &To)
40 {
41    if (From.IsOpen() == false || To.IsOpen() == false)
42       return false;
43    
44    // Buffered copy between fds
45    unsigned char *Buf = new unsigned char[64000];
46    unsigned long Size = From.Size();
47    while (Size != 0)
48    {
49       unsigned long ToRead = Size;
50       if (Size > 64000)
51          ToRead = 64000;
52       
53       if (From.Read(Buf,ToRead) == false || 
54           To.Write(Buf,ToRead) == false)
55       {
56          delete [] Buf;
57          return false;
58       }
59       
60       Size -= ToRead;
61    }
62
63    delete [] Buf;
64    return true;   
65 }
66                                                                         /*}}}*/
67 // GetLock - Gets a lock file                                           /*{{{*/
68 // ---------------------------------------------------------------------
69 /* This will create an empty file of the given name and lock it. Once this
70    is done all other calls to GetLock in any other process will fail with
71    -1. The return result is the fd of the file, the call should call
72    close at some time. */
73 int GetLock(string File,bool Errors)
74 {
75    int FD = open(File.c_str(),O_RDWR | O_CREAT | O_TRUNC,0640);
76    if (FD < 0)
77    {
78       if (Errors == true)
79          _error->Errno("open","Could not open lock file %s",File.c_str());
80       return -1;
81    }
82    
83    // Aquire a write lock
84    struct flock fl;
85    fl.l_type = F_WRLCK;
86    fl.l_whence = SEEK_SET;
87    fl.l_start = 0;
88    fl.l_len = 0;
89    if (fcntl(FD,F_SETLK,&fl) == -1)
90    {
91       if (errno == ENOLCK)
92       {
93          _error->Warning("Not using locking for nfs mounted lock file %s",File.c_str());
94          return true;
95       }      
96       if (Errors == true)
97          _error->Errno("open","Could not get lock %s",File.c_str());
98       close(FD);
99       return -1;
100    }
101
102    return FD;
103 }
104                                                                         /*}}}*/
105 // FileExists - Check if a file exists                                  /*{{{*/
106 // ---------------------------------------------------------------------
107 /* */
108 bool FileExists(string File)
109 {
110    struct stat Buf;
111    if (stat(File.c_str(),&Buf) != 0)
112       return false;
113    return true;
114 }
115                                                                         /*}}}*/
116 // SafeGetCWD - This is a safer getcwd that returns a dynamic string    /*{{{*/
117 // ---------------------------------------------------------------------
118 /* We return / on failure. */
119 string SafeGetCWD()
120 {
121    // Stash the current dir.
122    char S[300];
123    S[0] = 0;
124    if (getcwd(S,sizeof(S)-2) == 0)
125       return "/";
126    unsigned int Len = strlen(S);
127    S[Len] = '/';
128    S[Len+1] = 0;
129    return S;
130 }
131                                                                         /*}}}*/
132 // flNotDir - Strip the directory from the filename                     /*{{{*/
133 // ---------------------------------------------------------------------
134 /* */
135 string flNotDir(string File)
136 {
137    string::size_type Res = File.rfind('/');
138    if (Res == string::npos)
139       return File;
140    Res++;
141    return string(File,Res,Res - File.length());
142 }
143                                                                         /*}}}*/
144 // flNotFile - Strip the file from the directory name                   /*{{{*/
145 // ---------------------------------------------------------------------
146 /* */
147 string flNotFile(string File)
148 {
149    string::size_type Res = File.rfind('/');
150    if (Res == string::npos)
151       return File;
152    Res++;
153    return string(File,0,Res);
154 }
155                                                                         /*}}}*/
156 // flNoLink - If file is a symlink then deref it                        /*{{{*/
157 // ---------------------------------------------------------------------
158 /* If the name is not a link then the returned path is the input. */
159 string flNoLink(string File)
160 {
161    struct stat St;
162    if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
163       return File;
164    if (stat(File.c_str(),&St) != 0)
165       return File;
166    
167    /* Loop resolving the link. There is no need to limit the number of 
168       loops because the stat call above ensures that the symlink is not 
169       circular */
170    char Buffer[1024];
171    string NFile = File;
172    while (1)
173    {
174       // Read the link
175       int Res;
176       if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 || 
177           (unsigned)Res >= sizeof(Buffer))
178           return File;
179       
180       // Append or replace the previous path
181       Buffer[Res] = 0;
182       if (Buffer[0] == '/')
183          NFile = Buffer;
184       else
185          NFile = flNotFile(NFile) + Buffer;
186       
187       // See if we are done
188       if (lstat(NFile.c_str(),&St) != 0)
189          return File;
190       if (S_ISLNK(St.st_mode) == 0)
191          return NFile;      
192    }   
193 }
194                                                                         /*}}}*/
195 // SetCloseExec - Set the close on exec flag                            /*{{{*/
196 // ---------------------------------------------------------------------
197 /* */
198 void SetCloseExec(int Fd,bool Close)
199 {   
200    if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
201    {
202       cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
203       exit(100);
204    }
205 }
206                                                                         /*}}}*/
207 // SetNonBlock - Set the nonblocking flag                               /*{{{*/
208 // ---------------------------------------------------------------------
209 /* */
210 void SetNonBlock(int Fd,bool Block)
211 {   
212    int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
213    if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
214    {
215       cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
216       exit(100);
217    }
218 }
219                                                                         /*}}}*/
220 // WaitFd - Wait for a FD to become readable                            /*{{{*/
221 // ---------------------------------------------------------------------
222 /* This waits for a FD to become readable using select. It is usefull for
223    applications making use of non-blocking sockets. The timeout is 
224    in seconds. */
225 bool WaitFd(int Fd,bool write,unsigned long timeout)
226 {
227    fd_set Set;
228    struct timeval tv;
229    FD_ZERO(&Set);
230    FD_SET(Fd,&Set);
231    tv.tv_sec = timeout;
232    tv.tv_usec = 0;
233    if (write == true) 
234    {      
235       int Res;
236       do
237       {
238          Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
239       }
240       while (Res < 0 && errno == EINTR);
241       
242       if (Res <= 0)
243          return false;
244    } 
245    else 
246    {
247       int Res;
248       do
249       {
250          Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
251       }
252       while (Res < 0 && errno == EINTR);
253       
254       if (Res <= 0)
255          return false;
256    }
257    
258    return true;
259 }
260                                                                         /*}}}*/
261 // ExecFork - Magical fork that sanitizes the context before execing    /*{{{*/
262 // ---------------------------------------------------------------------
263 /* This is used if you want to cleanse the environment for the forked 
264    child, it fixes up the important signals and nukes all of the fds,
265    otherwise acts like normal fork. */
266 int ExecFork()
267 {
268    // Fork off the process
269    pid_t Process = fork();
270    if (Process < 0)
271    {
272       cerr << "FATAL -> Failed to fork." << endl;
273       exit(100);
274    }
275
276    // Spawn the subprocess
277    if (Process == 0)
278    {
279       // Setup the signals
280       signal(SIGPIPE,SIG_DFL);
281       signal(SIGQUIT,SIG_DFL);
282       signal(SIGINT,SIG_DFL);
283       signal(SIGWINCH,SIG_DFL);
284       signal(SIGCONT,SIG_DFL);
285       signal(SIGTSTP,SIG_DFL);
286       
287       // Close all of our FDs - just in case
288       for (int K = 3; K != 40; K++)
289          fcntl(K,F_SETFD,FD_CLOEXEC);
290    }
291    
292    return Process;
293 }
294                                                                         /*}}}*/
295 // ExecWait - Fancy waitpid                                             /*{{{*/
296 // ---------------------------------------------------------------------
297 /* Waits for the given sub process. If Reap is set the no errors are 
298    generated. Otherwise a failed subprocess will generate a proper descriptive
299    message */
300 bool ExecWait(int Pid,const char *Name,bool Reap)
301 {
302    if (Pid <= 1)
303       return true;
304    
305    // Wait and collect the error code
306    int Status;
307    while (waitpid(Pid,&Status,0) != Pid)
308    {
309       if (errno == EINTR)
310          continue;
311
312       if (Reap == true)
313          return false;
314       
315       return _error->Error("Waited, for %s but it wasn't there",Name);
316    }
317
318    
319    // Check for an error code.
320    if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
321    {
322       if (Reap == true)
323          return false;
324       if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
325          return _error->Error("Sub-process %s recieved a segmentation fault.",Name);
326
327       if (WIFEXITED(Status) != 0)
328          return _error->Error("Sub-process %s returned an error code (%u)",Name,WEXITSTATUS(Status));
329       
330       return _error->Error("Sub-process %s exited unexpectedly",Name);
331    }      
332    
333    return true;
334 }
335                                                                         /*}}}*/
336
337 // FileFd::Open - Open a file                                           /*{{{*/
338 // ---------------------------------------------------------------------
339 /* The most commonly used open mode combinations are given with Mode */
340 bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
341 {
342    Close();
343    Flags = AutoClose;
344    switch (Mode)
345    {
346       case ReadOnly:
347       iFd = open(FileName.c_str(),O_RDONLY);
348       break;
349       
350       case WriteEmpty:
351       {
352          struct stat Buf;
353          if (stat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
354             unlink(FileName.c_str());
355          iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
356          break;
357       }
358       
359       case WriteExists:
360       iFd = open(FileName.c_str(),O_RDWR);
361       break;
362
363       case WriteAny:
364       iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
365       break;      
366    }  
367
368    if (iFd < 0)
369       return _error->Errno("open","Could not open file %s",FileName.c_str());
370    
371    this->FileName = FileName;
372    SetCloseExec(iFd,true);
373    return true;
374 }
375                                                                         /*}}}*/
376 // FileFd::~File - Closes the file                                      /*{{{*/
377 // ---------------------------------------------------------------------
378 /* If the proper modes are selected then we close the Fd and possibly
379    unlink the file on error. */
380 FileFd::~FileFd()
381 {
382    Close();
383 }
384                                                                         /*}}}*/
385 // FileFd::Read - Read a bit of the file                                /*{{{*/
386 // ---------------------------------------------------------------------
387 /* We are carefull to handle interruption by a signal while reading 
388    gracefully. */
389 bool FileFd::Read(void *To,unsigned long Size,bool AllowEof)
390 {
391    int Res;
392    errno = 0;
393    do
394    {
395       Res = read(iFd,To,Size);
396       if (Res < 0 && errno == EINTR)
397          continue;
398       if (Res < 0)
399       {
400          Flags |= Fail;
401          return _error->Errno("read","Read error");
402       }
403       
404       To = (char *)To + Res;
405       Size -= Res;
406    }
407    while (Res > 0 && Size > 0);
408    
409    if (Size == 0)
410       return true;
411    
412    // Eof handling
413    if (AllowEof == true)
414    {
415       Flags |= HitEof;
416       return true;
417    }
418    
419    Flags |= Fail;
420    return _error->Error("read, still have %u to read but none left",Size);
421 }
422                                                                         /*}}}*/
423 // FileFd::Write - Write to the file                                    /*{{{*/
424 // ---------------------------------------------------------------------
425 /* */
426 bool FileFd::Write(const void *From,unsigned long Size)
427 {
428    int Res;
429    errno = 0;
430    do
431    {
432       Res = write(iFd,From,Size);
433       if (Res < 0 && errno == EINTR)
434          continue;
435       if (Res < 0)
436       {
437          Flags |= Fail;
438          return _error->Errno("write","Write error");
439       }
440       
441       From = (char *)From + Res;
442       Size -= Res;
443    }
444    while (Res > 0 && Size > 0);
445    
446    if (Size == 0)
447       return true;
448    
449    Flags |= Fail;
450    return _error->Error("write, still have %u to write but couldn't",Size);
451 }
452                                                                         /*}}}*/
453 // FileFd::Seek - Seek in the file                                      /*{{{*/
454 // ---------------------------------------------------------------------
455 /* */
456 bool FileFd::Seek(unsigned long To)
457 {
458    if (lseek(iFd,To,SEEK_SET) != (signed)To)
459    {
460       Flags |= Fail;
461       return _error->Error("Unable to seek to %u",To);
462    }
463    
464    return true;
465 }
466                                                                         /*}}}*/
467 // FileFd::Skip - Seek in the file                                      /*{{{*/
468 // ---------------------------------------------------------------------
469 /* */
470 bool FileFd::Skip(unsigned long Over)
471 {
472    if (lseek(iFd,Over,SEEK_CUR) < 0)
473    {
474       Flags |= Fail;
475       return _error->Error("Unable to seek ahead %u",Over);
476    }
477    
478    return true;
479 }
480                                                                         /*}}}*/
481 // FileFd::Truncate - Truncate the file                                 /*{{{*/
482 // ---------------------------------------------------------------------
483 /* */
484 bool FileFd::Truncate(unsigned long To)
485 {
486    if (ftruncate(iFd,To) != 0)
487    {
488       Flags |= Fail;
489       return _error->Error("Unable to truncate to %u",To);
490    }
491    
492    return true;
493 }
494                                                                         /*}}}*/
495 // FileFd::Tell - Current seek position                                 /*{{{*/
496 // ---------------------------------------------------------------------
497 /* */
498 unsigned long FileFd::Tell()
499 {
500    off_t Res = lseek(iFd,0,SEEK_CUR);
501    if (Res == (off_t)-1)
502       _error->Errno("lseek","Failed to determine the current file position");
503    return Res;
504 }
505                                                                         /*}}}*/
506 // FileFd::Size - Return the size of the file                           /*{{{*/
507 // ---------------------------------------------------------------------
508 /* */
509 unsigned long FileFd::Size()
510 {
511    struct stat Buf;
512    if (fstat(iFd,&Buf) != 0)
513       return _error->Errno("fstat","Unable to determine the file size");
514    return Buf.st_size;
515 }
516                                                                         /*}}}*/
517 // FileFd::Close - Close the file if the close flag is set              /*{{{*/
518 // ---------------------------------------------------------------------
519 /* */
520 bool FileFd::Close()
521 {
522    bool Res = true;
523    if ((Flags & AutoClose) == AutoClose)
524       if (iFd >= 0 && close(iFd) != 0)
525          Res &= _error->Errno("close","Problem closing the file");
526    iFd = -1;
527    
528    if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
529        FileName.empty() == false)
530       if (unlink(FileName.c_str()) != 0)
531          Res &= _error->Warning("unlnk","Problem unlinking the file");
532    return Res;
533 }
534                                                                         /*}}}*/