]>
git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
0b0739cda5a7ad7d5f0a8f73ffbd07423485f992
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>.
15 The exception is RunScripts() it is under the GPLv2
17 ##################################################################### */
19 // Include Files /*{{{*/
20 #include <apt-pkg/fileutl.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/sptr.h>
24 #include <apt-pkg/configuration.h>
36 #include <sys/types.h>
48 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
49 // ---------------------------------------------------------------------
51 bool RunScripts(const char *Cnf
)
53 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
54 if (Opts
== 0 || Opts
->Child
== 0)
58 // Fork for running the system calls
59 pid_t Child
= ExecFork();
64 if (chdir("/tmp/") != 0)
67 unsigned int Count
= 1;
68 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
70 if (Opts
->Value
.empty() == true)
73 if (system(Opts
->Value
.c_str()) != 0)
81 while (waitpid(Child
,&Status
,0) != Child
)
85 return _error
->Errno("waitpid","Couldn't wait for subprocess");
88 // Restore sig int/quit
89 signal(SIGQUIT
,SIG_DFL
);
90 signal(SIGINT
,SIG_DFL
);
92 // Check for an error code.
93 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
95 unsigned int Count
= WEXITSTATUS(Status
);
99 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
100 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
103 return _error
->Error("Sub-process returned an error code");
110 // CopyFile - Buffered copy of a file /*{{{*/
111 // ---------------------------------------------------------------------
112 /* The caller is expected to set things so that failure causes erasure */
113 bool CopyFile(FileFd
&From
,FileFd
&To
)
115 if (From
.IsOpen() == false || To
.IsOpen() == false)
118 // Buffered copy between fds
119 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
120 unsigned long Size
= From
.Size();
123 unsigned long ToRead
= Size
;
127 if (From
.Read(Buf
,ToRead
) == false ||
128 To
.Write(Buf
,ToRead
) == false)
137 // GetLock - Gets a lock file /*{{{*/
138 // ---------------------------------------------------------------------
139 /* This will create an empty file of the given name and lock it. Once this
140 is done all other calls to GetLock in any other process will fail with
141 -1. The return result is the fd of the file, the call should call
142 close at some time. */
143 int GetLock(string File
,bool Errors
)
145 // GetLock() is used in aptitude on directories with public-write access
146 // Use O_NOFOLLOW here to prevent symlink traversal attacks
147 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
150 // Read only .. cant have locking problems there.
153 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
154 return dup(0); // Need something for the caller to close
158 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
160 // Feh.. We do this to distinguish the lock vs open case..
164 SetCloseExec(FD
,true);
166 // Aquire a write lock
169 fl
.l_whence
= SEEK_SET
;
172 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
176 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
177 return dup(0); // Need something for the caller to close
180 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
191 // FileExists - Check if a file exists /*{{{*/
192 // ---------------------------------------------------------------------
194 bool FileExists(string File
)
197 if (stat(File
.c_str(),&Buf
) != 0)
202 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
203 // ---------------------------------------------------------------------
205 bool DirectoryExists(string
const &Path
)
208 if (stat(Path
.c_str(),&Buf
) != 0)
210 return ((Buf
.st_mode
& S_IFDIR
) != 0);
213 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
214 // ---------------------------------------------------------------------
215 /* This method will create all directories needed for path in good old
216 mkdir -p style but refuses to do this if Parent is not a prefix of
217 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
218 so it will create apt/archives if /var/cache exists - on the other
219 hand if the parent is /var/lib the creation will fail as this path
220 is not a parent of the path to be generated. */
221 bool CreateDirectory(string
const &Parent
, string
const &Path
)
223 if (Parent
.empty() == true || Path
.empty() == true)
226 if (DirectoryExists(Path
) == true)
229 if (DirectoryExists(Parent
) == false)
232 // we are not going to create directories "into the blue"
233 if (Path
.find(Parent
, 0) != 0)
236 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
237 string progress
= Parent
;
238 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
240 if (d
->empty() == true)
243 progress
.append("/").append(*d
);
244 if (DirectoryExists(progress
) == true)
247 if (mkdir(progress
.c_str(), 0755) != 0)
253 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
254 // ---------------------------------------------------------------------
255 /* If an extension is given only files with this extension are included
256 in the returned vector, otherwise every "normal" file is included. */
257 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
258 bool const &SortList
, bool const &AllowNoExt
)
260 std::vector
<string
> ext
;
262 if (Ext
.empty() == false)
264 if (AllowNoExt
== true && ext
.empty() == false)
266 return GetListOfFilesInDir(Dir
, ext
, SortList
);
268 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
269 bool const &SortList
)
271 // Attention debuggers: need to be set with the environment config file!
272 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
275 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
276 if (Ext
.empty() == true)
277 std::clog
<< "\tNO extension" << std::endl
;
279 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
281 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
284 std::vector
<string
> List
;
285 DIR *D
= opendir(Dir
.c_str());
288 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
292 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
294 // skip "hidden" files
295 if (Ent
->d_name
[0] == '.')
298 // check for accepted extension:
299 // no extension given -> periods are bad as hell!
300 // extensions given -> "" extension allows no extension
301 if (Ext
.empty() == false)
303 string d_ext
= flExtension(Ent
->d_name
);
304 if (d_ext
== Ent
->d_name
) // no extension
306 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
309 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
313 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
316 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
321 // Skip bad filenames ala run-parts
322 const char *C
= Ent
->d_name
;
324 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
325 && *C
!= '_' && *C
!= '-') {
326 // no required extension -> dot is a bad character
327 if (*C
== '.' && Ext
.empty() == false)
332 // we don't reach the end of the name -> bad character included
336 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
337 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
341 // skip filenames which end with a period. These are never valid
345 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
349 // Make sure it is a file and not something else
350 string
const File
= flCombine(Dir
,Ent
->d_name
);
352 if (stat(File
.c_str(),&St
) != 0 || S_ISREG(St
.st_mode
) == 0)
355 std::clog
<< "Bad file: " << Ent
->d_name
<< " → stat says not a good file" << std::endl
;
360 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
361 List
.push_back(File
);
365 if (SortList
== true)
366 std::sort(List
.begin(),List
.end());
370 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
371 // ---------------------------------------------------------------------
372 /* We return / on failure. */
375 // Stash the current dir.
378 if (getcwd(S
,sizeof(S
)-2) == 0)
380 unsigned int Len
= strlen(S
);
386 // flNotDir - Strip the directory from the filename /*{{{*/
387 // ---------------------------------------------------------------------
389 string
flNotDir(string File
)
391 string::size_type Res
= File
.rfind('/');
392 if (Res
== string::npos
)
395 return string(File
,Res
,Res
- File
.length());
398 // flNotFile - Strip the file from the directory name /*{{{*/
399 // ---------------------------------------------------------------------
400 /* Result ends in a / */
401 string
flNotFile(string File
)
403 string::size_type Res
= File
.rfind('/');
404 if (Res
== string::npos
)
407 return string(File
,0,Res
);
410 // flExtension - Return the extension for the file /*{{{*/
411 // ---------------------------------------------------------------------
413 string
flExtension(string File
)
415 string::size_type Res
= File
.rfind('.');
416 if (Res
== string::npos
)
419 return string(File
,Res
,Res
- File
.length());
422 // flNoLink - If file is a symlink then deref it /*{{{*/
423 // ---------------------------------------------------------------------
424 /* If the name is not a link then the returned path is the input. */
425 string
flNoLink(string File
)
428 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
430 if (stat(File
.c_str(),&St
) != 0)
433 /* Loop resolving the link. There is no need to limit the number of
434 loops because the stat call above ensures that the symlink is not
442 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
443 (unsigned)Res
>= sizeof(Buffer
))
446 // Append or replace the previous path
448 if (Buffer
[0] == '/')
451 NFile
= flNotFile(NFile
) + Buffer
;
453 // See if we are done
454 if (lstat(NFile
.c_str(),&St
) != 0)
456 if (S_ISLNK(St
.st_mode
) == 0)
461 // flCombine - Combine a file and a directory /*{{{*/
462 // ---------------------------------------------------------------------
463 /* If the file is an absolute path then it is just returned, otherwise
464 the directory is pre-pended to it. */
465 string
flCombine(string Dir
,string File
)
467 if (File
.empty() == true)
470 if (File
[0] == '/' || Dir
.empty() == true)
472 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
474 if (Dir
[Dir
.length()-1] == '/')
476 return Dir
+ '/' + File
;
479 // SetCloseExec - Set the close on exec flag /*{{{*/
480 // ---------------------------------------------------------------------
482 void SetCloseExec(int Fd
,bool Close
)
484 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
486 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
491 // SetNonBlock - Set the nonblocking flag /*{{{*/
492 // ---------------------------------------------------------------------
494 void SetNonBlock(int Fd
,bool Block
)
496 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
497 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
499 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
504 // WaitFd - Wait for a FD to become readable /*{{{*/
505 // ---------------------------------------------------------------------
506 /* This waits for a FD to become readable using select. It is useful for
507 applications making use of non-blocking sockets. The timeout is
509 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
522 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
524 while (Res
< 0 && errno
== EINTR
);
534 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
536 while (Res
< 0 && errno
== EINTR
);
545 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
546 // ---------------------------------------------------------------------
547 /* This is used if you want to cleanse the environment for the forked
548 child, it fixes up the important signals and nukes all of the fds,
549 otherwise acts like normal fork. */
552 // Fork off the process
553 pid_t Process
= fork();
556 cerr
<< "FATAL -> Failed to fork." << endl
;
560 // Spawn the subprocess
564 signal(SIGPIPE
,SIG_DFL
);
565 signal(SIGQUIT
,SIG_DFL
);
566 signal(SIGINT
,SIG_DFL
);
567 signal(SIGWINCH
,SIG_DFL
);
568 signal(SIGCONT
,SIG_DFL
);
569 signal(SIGTSTP
,SIG_DFL
);
572 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
573 if (Opts
!= 0 && Opts
->Child
!= 0)
576 for (; Opts
!= 0; Opts
= Opts
->Next
)
578 if (Opts
->Value
.empty() == true)
580 int fd
= atoi(Opts
->Value
.c_str());
585 // Close all of our FDs - just in case
586 for (int K
= 3; K
!= 40; K
++)
588 if(KeepFDs
.find(K
) == KeepFDs
.end())
589 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
596 // ExecWait - Fancy waitpid /*{{{*/
597 // ---------------------------------------------------------------------
598 /* Waits for the given sub process. If Reap is set then no errors are
599 generated. Otherwise a failed subprocess will generate a proper descriptive
601 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
606 // Wait and collect the error code
608 while (waitpid(Pid
,&Status
,0) != Pid
)
616 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
620 // Check for an error code.
621 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
625 if (WIFSIGNALED(Status
) != 0)
627 if( WTERMSIG(Status
) == SIGSEGV
)
628 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
630 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
633 if (WIFEXITED(Status
) != 0)
634 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
636 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
643 // FileFd::Open - Open a file /*{{{*/
644 // ---------------------------------------------------------------------
645 /* The most commonly used open mode combinations are given with Mode */
646 bool FileFd::Open(string FileName
,OpenMode Mode
, unsigned long Perms
)
653 iFd
= open(FileName
.c_str(),O_RDONLY
);
659 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
660 TemporaryFileName
= string(mktemp(name
));
661 iFd
= open(TemporaryFileName
.c_str(),O_RDWR
| O_CREAT
| O_EXCL
,Perms
);
667 iFd
= open(FileName
.c_str(),O_RDWR
);
671 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
,Perms
);
675 unlink(FileName
.c_str());
676 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_EXCL
,Perms
);
681 return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str());
683 this->FileName
= FileName
;
684 SetCloseExec(iFd
,true);
688 // FileFd::~File - Closes the file /*{{{*/
689 // ---------------------------------------------------------------------
690 /* If the proper modes are selected then we close the Fd and possibly
691 unlink the file on error. */
697 // FileFd::Read - Read a bit of the file /*{{{*/
698 // ---------------------------------------------------------------------
699 /* We are carefull to handle interruption by a signal while reading
701 bool FileFd::Read(void *To
,unsigned long Size
,unsigned long *Actual
)
710 Res
= read(iFd
,To
,Size
);
711 if (Res
< 0 && errno
== EINTR
)
716 return _error
->Errno("read",_("Read error"));
719 To
= (char *)To
+ Res
;
724 while (Res
> 0 && Size
> 0);
737 return _error
->Error(_("read, still have %lu to read but none left"),Size
);
740 // FileFd::Write - Write to the file /*{{{*/
741 // ---------------------------------------------------------------------
743 bool FileFd::Write(const void *From
,unsigned long Size
)
749 Res
= write(iFd
,From
,Size
);
750 if (Res
< 0 && errno
== EINTR
)
755 return _error
->Errno("write",_("Write error"));
758 From
= (char *)From
+ Res
;
761 while (Res
> 0 && Size
> 0);
767 return _error
->Error(_("write, still have %lu to write but couldn't"),Size
);
770 // FileFd::Seek - Seek in the file /*{{{*/
771 // ---------------------------------------------------------------------
773 bool FileFd::Seek(unsigned long To
)
775 if (lseek(iFd
,To
,SEEK_SET
) != (signed)To
)
778 return _error
->Error("Unable to seek to %lu",To
);
784 // FileFd::Skip - Seek in the file /*{{{*/
785 // ---------------------------------------------------------------------
787 bool FileFd::Skip(unsigned long Over
)
789 if (lseek(iFd
,Over
,SEEK_CUR
) < 0)
792 return _error
->Error("Unable to seek ahead %lu",Over
);
798 // FileFd::Truncate - Truncate the file /*{{{*/
799 // ---------------------------------------------------------------------
801 bool FileFd::Truncate(unsigned long To
)
803 if (ftruncate(iFd
,To
) != 0)
806 return _error
->Error("Unable to truncate to %lu",To
);
812 // FileFd::Tell - Current seek position /*{{{*/
813 // ---------------------------------------------------------------------
815 unsigned long FileFd::Tell()
817 off_t Res
= lseek(iFd
,0,SEEK_CUR
);
818 if (Res
== (off_t
)-1)
819 _error
->Errno("lseek","Failed to determine the current file position");
823 // FileFd::Size - Return the size of the file /*{{{*/
824 // ---------------------------------------------------------------------
826 unsigned long FileFd::Size()
829 if (fstat(iFd
,&Buf
) != 0)
830 return _error
->Errno("fstat","Unable to determine the file size");
834 // FileFd::Close - Close the file if the close flag is set /*{{{*/
835 // ---------------------------------------------------------------------
840 if ((Flags
& AutoClose
) == AutoClose
)
841 if (iFd
>= 0 && close(iFd
) != 0)
842 Res
&= _error
->Errno("close",_("Problem closing the file"));
845 if ((Flags
& Replace
) == Replace
) {
846 FileName
= TemporaryFileName
; // for the unlink() below.
847 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
848 Res
&= _error
->Errno("rename",_("Problem renaming the file"));
851 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
852 FileName
.empty() == false)
853 if (unlink(FileName
.c_str()) != 0)
854 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file"));
860 // FileFd::Sync - Sync the file /*{{{*/
861 // ---------------------------------------------------------------------
865 #ifdef _POSIX_SYNCHRONIZED_IO
867 return _error
->Errno("sync",_("Problem syncing the file"));