]>
git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
4 /* ######################################################################
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
11 Most of this source is placed in the Public Domain, do with it what
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
16 The exception is RunScripts() it is under the GPLv2
18 ##################################################################### */
20 // Include Files /*{{{*/
21 #include <apt-pkg/fileutl.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/sptr.h>
24 #include <apt-pkg/configuration.h>
35 #include <sys/types.h>
47 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
48 // ---------------------------------------------------------------------
50 bool RunScripts(const char *Cnf
)
52 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
53 if (Opts
== 0 || Opts
->Child
== 0)
57 // Fork for running the system calls
58 pid_t Child
= ExecFork();
63 if (chdir("/tmp/") != 0)
66 unsigned int Count
= 1;
67 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
69 if (Opts
->Value
.empty() == true)
72 if (system(Opts
->Value
.c_str()) != 0)
80 while (waitpid(Child
,&Status
,0) != Child
)
84 return _error
->Errno("waitpid","Couldn't wait for subprocess");
87 // Restore sig int/quit
88 signal(SIGQUIT
,SIG_DFL
);
89 signal(SIGINT
,SIG_DFL
);
91 // Check for an error code.
92 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
94 unsigned int Count
= WEXITSTATUS(Status
);
98 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
99 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
102 return _error
->Error("Sub-process returned an error code");
109 // CopyFile - Buffered copy of a file /*{{{*/
110 // ---------------------------------------------------------------------
111 /* The caller is expected to set things so that failure causes erasure */
112 bool CopyFile(FileFd
&From
,FileFd
&To
)
114 if (From
.IsOpen() == false || To
.IsOpen() == false)
117 // Buffered copy between fds
118 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
119 unsigned long Size
= From
.Size();
122 unsigned long ToRead
= Size
;
126 if (From
.Read(Buf
,ToRead
) == false ||
127 To
.Write(Buf
,ToRead
) == false)
136 // GetLock - Gets a lock file /*{{{*/
137 // ---------------------------------------------------------------------
138 /* This will create an empty file of the given name and lock it. Once this
139 is done all other calls to GetLock in any other process will fail with
140 -1. The return result is the fd of the file, the call should call
141 close at some time. */
142 int GetLock(string File
,bool Errors
)
144 // GetLock() is used in aptitude on directories with public-write access
145 // Use O_NOFOLLOW here to prevent symlink traversal attacks
146 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
149 // Read only .. cant have locking problems there.
152 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
153 return dup(0); // Need something for the caller to close
157 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
159 // Feh.. We do this to distinguish the lock vs open case..
163 SetCloseExec(FD
,true);
165 // Aquire a write lock
168 fl
.l_whence
= SEEK_SET
;
171 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
175 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
176 return dup(0); // Need something for the caller to close
179 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
190 // FileExists - Check if a file exists /*{{{*/
191 // ---------------------------------------------------------------------
193 bool FileExists(string File
)
196 if (stat(File
.c_str(),&Buf
) != 0)
201 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
202 // ---------------------------------------------------------------------
203 /* If an extension is given only files with this extension are included
204 in the returned vector, otherwise every "normal" file is included. */
205 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
206 bool const &SortList
)
208 return GetListOfFilesInDir(Dir
, Ext
, SortList
, false);
210 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
211 bool const &SortList
, bool const &AllowNoExt
)
213 std::vector
<string
> ext
;
215 if (Ext
.empty() == false)
217 if (AllowNoExt
== true && ext
.empty() == false)
219 return GetListOfFilesInDir(Dir
, ext
, SortList
);
221 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
222 bool const &SortList
)
224 // Attention debuggers: need to be set with the environment config file!
225 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
228 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
229 if (Ext
.empty() == true)
230 std::clog
<< "\tNO extension" << std::endl
;
232 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
234 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
237 std::vector
<string
> List
;
238 DIR *D
= opendir(Dir
.c_str());
241 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
245 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
247 // skip "hidden" files
248 if (Ent
->d_name
[0] == '.')
251 // check for accepted extension:
252 // no extension given -> periods are bad as hell!
253 // extensions given -> "" extension allows no extension
254 if (Ext
.empty() == false)
256 string d_ext
= flExtension(Ent
->d_name
);
257 if (d_ext
== Ent
->d_name
) // no extension
259 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
262 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
266 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
269 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
274 // Skip bad filenames ala run-parts
275 const char *C
= Ent
->d_name
;
277 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
278 && *C
!= '_' && *C
!= '-') {
279 // no required extension -> dot is a bad character
280 if (*C
== '.' && Ext
.empty() == false)
285 // we don't reach the end of the name -> bad character included
289 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
290 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
294 // skip filenames which end with a period. These are never valid
298 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
302 // Make sure it is a file and not something else
303 string
const File
= flCombine(Dir
,Ent
->d_name
);
305 if (stat(File
.c_str(),&St
) != 0 || S_ISREG(St
.st_mode
) == 0)
308 std::clog
<< "Bad file: " << Ent
->d_name
<< " → stat says not a good file" << std::endl
;
313 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
314 List
.push_back(File
);
318 if (SortList
== true)
319 std::sort(List
.begin(),List
.end());
323 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
324 // ---------------------------------------------------------------------
325 /* We return / on failure. */
328 // Stash the current dir.
331 if (getcwd(S
,sizeof(S
)-2) == 0)
333 unsigned int Len
= strlen(S
);
339 // flNotDir - Strip the directory from the filename /*{{{*/
340 // ---------------------------------------------------------------------
342 string
flNotDir(string File
)
344 string::size_type Res
= File
.rfind('/');
345 if (Res
== string::npos
)
348 return string(File
,Res
,Res
- File
.length());
351 // flNotFile - Strip the file from the directory name /*{{{*/
352 // ---------------------------------------------------------------------
353 /* Result ends in a / */
354 string
flNotFile(string File
)
356 string::size_type Res
= File
.rfind('/');
357 if (Res
== string::npos
)
360 return string(File
,0,Res
);
363 // flExtension - Return the extension for the file /*{{{*/
364 // ---------------------------------------------------------------------
366 string
flExtension(string File
)
368 string::size_type Res
= File
.rfind('.');
369 if (Res
== string::npos
)
372 return string(File
,Res
,Res
- File
.length());
375 // flNoLink - If file is a symlink then deref it /*{{{*/
376 // ---------------------------------------------------------------------
377 /* If the name is not a link then the returned path is the input. */
378 string
flNoLink(string File
)
381 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
383 if (stat(File
.c_str(),&St
) != 0)
386 /* Loop resolving the link. There is no need to limit the number of
387 loops because the stat call above ensures that the symlink is not
395 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
396 (unsigned)Res
>= sizeof(Buffer
))
399 // Append or replace the previous path
401 if (Buffer
[0] == '/')
404 NFile
= flNotFile(NFile
) + Buffer
;
406 // See if we are done
407 if (lstat(NFile
.c_str(),&St
) != 0)
409 if (S_ISLNK(St
.st_mode
) == 0)
414 // flCombine - Combine a file and a directory /*{{{*/
415 // ---------------------------------------------------------------------
416 /* If the file is an absolute path then it is just returned, otherwise
417 the directory is pre-pended to it. */
418 string
flCombine(string Dir
,string File
)
420 if (File
.empty() == true)
423 if (File
[0] == '/' || Dir
.empty() == true)
425 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
427 if (Dir
[Dir
.length()-1] == '/')
429 return Dir
+ '/' + File
;
432 // SetCloseExec - Set the close on exec flag /*{{{*/
433 // ---------------------------------------------------------------------
435 void SetCloseExec(int Fd
,bool Close
)
437 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
439 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
444 // SetNonBlock - Set the nonblocking flag /*{{{*/
445 // ---------------------------------------------------------------------
447 void SetNonBlock(int Fd
,bool Block
)
449 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
450 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
452 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
457 // WaitFd - Wait for a FD to become readable /*{{{*/
458 // ---------------------------------------------------------------------
459 /* This waits for a FD to become readable using select. It is useful for
460 applications making use of non-blocking sockets. The timeout is
462 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
475 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
477 while (Res
< 0 && errno
== EINTR
);
487 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
489 while (Res
< 0 && errno
== EINTR
);
498 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
499 // ---------------------------------------------------------------------
500 /* This is used if you want to cleanse the environment for the forked
501 child, it fixes up the important signals and nukes all of the fds,
502 otherwise acts like normal fork. */
505 // Fork off the process
506 pid_t Process
= fork();
509 cerr
<< "FATAL -> Failed to fork." << endl
;
513 // Spawn the subprocess
517 signal(SIGPIPE
,SIG_DFL
);
518 signal(SIGQUIT
,SIG_DFL
);
519 signal(SIGINT
,SIG_DFL
);
520 signal(SIGWINCH
,SIG_DFL
);
521 signal(SIGCONT
,SIG_DFL
);
522 signal(SIGTSTP
,SIG_DFL
);
525 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
526 if (Opts
!= 0 && Opts
->Child
!= 0)
529 for (; Opts
!= 0; Opts
= Opts
->Next
)
531 if (Opts
->Value
.empty() == true)
533 int fd
= atoi(Opts
->Value
.c_str());
538 // Close all of our FDs - just in case
539 for (int K
= 3; K
!= 40; K
++)
541 if(KeepFDs
.find(K
) == KeepFDs
.end())
542 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
549 // ExecWait - Fancy waitpid /*{{{*/
550 // ---------------------------------------------------------------------
551 /* Waits for the given sub process. If Reap is set then no errors are
552 generated. Otherwise a failed subprocess will generate a proper descriptive
554 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
559 // Wait and collect the error code
561 while (waitpid(Pid
,&Status
,0) != Pid
)
569 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
573 // Check for an error code.
574 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
578 if (WIFSIGNALED(Status
) != 0)
580 if( WTERMSIG(Status
) == SIGSEGV
)
581 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
583 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
586 if (WIFEXITED(Status
) != 0)
587 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
589 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
596 // FileFd::Open - Open a file /*{{{*/
597 // ---------------------------------------------------------------------
598 /* The most commonly used open mode combinations are given with Mode */
599 bool FileFd::Open(string FileName
,OpenMode Mode
, unsigned long Perms
)
606 iFd
= open(FileName
.c_str(),O_RDONLY
);
607 if (iFd
> 0 && FileName
.compare(FileName
.size()-3, 3, ".gz") == 0) {
608 gz
= gzdopen (iFd
, "r");
619 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
620 unlink(FileName
.c_str());
621 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_TRUNC
,Perms
);
626 iFd
= open(FileName
.c_str(),O_RDWR
);
630 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
,Perms
);
634 unlink(FileName
.c_str());
635 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_EXCL
,Perms
);
640 return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str());
642 this->FileName
= FileName
;
643 SetCloseExec(iFd
,true);
647 // FileFd::~File - Closes the file /*{{{*/
648 // ---------------------------------------------------------------------
649 /* If the proper modes are selected then we close the Fd and possibly
650 unlink the file on error. */
656 // FileFd::Read - Read a bit of the file /*{{{*/
657 // ---------------------------------------------------------------------
658 /* We are carefull to handle interruption by a signal while reading
660 bool FileFd::Read(void *To
,unsigned long Size
,unsigned long *Actual
)
670 Res
= gzread(gz
,To
,Size
);
672 Res
= read(iFd
,To
,Size
);
673 if (Res
< 0 && errno
== EINTR
)
678 return _error
->Errno("read",_("Read error"));
681 To
= (char *)To
+ Res
;
686 while (Res
> 0 && Size
> 0);
699 return _error
->Error(_("read, still have %lu to read but none left"),Size
);
702 // FileFd::Write - Write to the file /*{{{*/
703 // ---------------------------------------------------------------------
705 bool FileFd::Write(const void *From
,unsigned long Size
)
712 Res
= gzwrite(gz
,From
,Size
);
714 Res
= write(iFd
,From
,Size
);
715 if (Res
< 0 && errno
== EINTR
)
720 return _error
->Errno("write",_("Write error"));
723 From
= (char *)From
+ Res
;
726 while (Res
> 0 && Size
> 0);
732 return _error
->Error(_("write, still have %lu to write but couldn't"),Size
);
735 // FileFd::Seek - Seek in the file /*{{{*/
736 // ---------------------------------------------------------------------
738 bool FileFd::Seek(unsigned long To
)
742 res
= gzseek(gz
,To
,SEEK_SET
);
744 res
= lseek(iFd
,To
,SEEK_SET
);
745 if (res
!= (signed)To
)
748 return _error
->Error("Unable to seek to %lu",To
);
754 // FileFd::Skip - Seek in the file /*{{{*/
755 // ---------------------------------------------------------------------
757 bool FileFd::Skip(unsigned long Over
)
761 res
= gzseek(gz
,Over
,SEEK_CUR
);
763 res
= lseek(iFd
,Over
,SEEK_CUR
);
767 return _error
->Error("Unable to seek ahead %lu",Over
);
773 // FileFd::Truncate - Truncate the file /*{{{*/
774 // ---------------------------------------------------------------------
776 bool FileFd::Truncate(unsigned long To
)
781 return _error
->Error("Truncating gzipped files is not implemented (%s)", FileName
.c_str());
783 if (ftruncate(iFd
,To
) != 0)
786 return _error
->Error("Unable to truncate to %lu",To
);
792 // FileFd::Tell - Current seek position /*{{{*/
793 // ---------------------------------------------------------------------
795 unsigned long FileFd::Tell()
801 Res
= lseek(iFd
,0,SEEK_CUR
);
802 if (Res
== (off_t
)-1)
803 _error
->Errno("lseek","Failed to determine the current file position");
807 // FileFd::Size - Return the size of the file /*{{{*/
808 // ---------------------------------------------------------------------
810 unsigned long FileFd::Size()
812 //TODO: For gz, do we need the actual file size here or the uncompressed length?
814 if (fstat(iFd
,&Buf
) != 0)
815 return _error
->Errno("fstat","Unable to determine the file size");
819 // FileFd::Close - Close the file if the close flag is set /*{{{*/
820 // ---------------------------------------------------------------------
825 if ((Flags
& AutoClose
) == AutoClose
)
826 if ((gz
!= NULL
&& gzclose(gz
) != 0) || (gz
== NULL
&& iFd
> 0 && close(iFd
) != 0))
827 Res
&= _error
->Errno("close",_("Problem closing the file"));
831 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
832 FileName
.empty() == false)
833 if (unlink(FileName
.c_str()) != 0)
834 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file"));
838 // FileFd::Sync - Sync the file /*{{{*/
839 // ---------------------------------------------------------------------
843 #ifdef _POSIX_SYNCHRONIZED_IO
845 return _error
->Errno("sync",_("Problem syncing the file"));