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