]>
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>.
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>
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 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
202 // ---------------------------------------------------------------------
204 bool DirectoryExists(string
const &Path
)
207 if (stat(Path
.c_str(),&Buf
) != 0)
209 return ((Buf
.st_mode
& S_IFDIR
) != 0);
212 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
213 // ---------------------------------------------------------------------
214 /* This method will create all directories needed for path in good old
215 mkdir -p style but refuses to do this if Parent is not a prefix of
216 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
217 so it will create apt/archives if /var/cache exists - on the other
218 hand if the parent is /var/lib the creation will fail as this path
219 is not a parent of the path to be generated. */
220 bool CreateDirectory(string
const &Parent
, string
const &Path
)
222 if (Parent
.empty() == true || Path
.empty() == true)
225 if (DirectoryExists(Path
) == true)
228 if (DirectoryExists(Parent
) == false)
231 // we are not going to create directories "into the blue"
232 if (Path
.find(Parent
, 0) != 0)
235 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
236 string progress
= Parent
;
237 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
239 if (d
->empty() == true)
242 progress
.append("/").append(*d
);
243 if (DirectoryExists(progress
) == true)
246 if (mkdir(progress
.c_str(), 0755) != 0)
252 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
253 // ---------------------------------------------------------------------
254 /* If an extension is given only files with this extension are included
255 in the returned vector, otherwise every "normal" file is included. */
256 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
257 bool const &SortList
, bool const &AllowNoExt
)
259 std::vector
<string
> ext
;
261 if (Ext
.empty() == false)
263 if (AllowNoExt
== true && ext
.empty() == false)
265 return GetListOfFilesInDir(Dir
, ext
, SortList
);
267 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
268 bool const &SortList
)
270 // Attention debuggers: need to be set with the environment config file!
271 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
274 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
275 if (Ext
.empty() == true)
276 std::clog
<< "\tNO extension" << std::endl
;
278 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
280 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
283 std::vector
<string
> List
;
284 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
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
;
310 _error
->Notice("Ignoring file '%s' in directory '%s' as it has no filename extension", Ent
->d_name
, Dir
.c_str());
314 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
317 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
318 if (SilentIgnore
.Match(Ent
->d_name
) == false)
319 _error
->Notice("Ignoring file '%s' in directory '%s' as it has an invalid filename extension", Ent
->d_name
, Dir
.c_str());
324 // Skip bad filenames ala run-parts
325 const char *C
= Ent
->d_name
;
327 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
328 && *C
!= '_' && *C
!= '-') {
329 // no required extension -> dot is a bad character
330 if (*C
== '.' && Ext
.empty() == false)
335 // we don't reach the end of the name -> bad character included
339 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
340 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
344 // skip filenames which end with a period. These are never valid
348 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
352 // Make sure it is a file and not something else
353 string
const File
= flCombine(Dir
,Ent
->d_name
);
355 if (stat(File
.c_str(),&St
) != 0 || S_ISREG(St
.st_mode
) == 0)
358 std::clog
<< "Bad file: " << Ent
->d_name
<< " → stat says not a good file" << std::endl
;
363 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
364 List
.push_back(File
);
368 if (SortList
== true)
369 std::sort(List
.begin(),List
.end());
373 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
374 // ---------------------------------------------------------------------
375 /* We return / on failure. */
378 // Stash the current dir.
381 if (getcwd(S
,sizeof(S
)-2) == 0)
383 unsigned int Len
= strlen(S
);
389 // flNotDir - Strip the directory from the filename /*{{{*/
390 // ---------------------------------------------------------------------
392 string
flNotDir(string File
)
394 string::size_type Res
= File
.rfind('/');
395 if (Res
== string::npos
)
398 return string(File
,Res
,Res
- File
.length());
401 // flNotFile - Strip the file from the directory name /*{{{*/
402 // ---------------------------------------------------------------------
403 /* Result ends in a / */
404 string
flNotFile(string File
)
406 string::size_type Res
= File
.rfind('/');
407 if (Res
== string::npos
)
410 return string(File
,0,Res
);
413 // flExtension - Return the extension for the file /*{{{*/
414 // ---------------------------------------------------------------------
416 string
flExtension(string File
)
418 string::size_type Res
= File
.rfind('.');
419 if (Res
== string::npos
)
422 return string(File
,Res
,Res
- File
.length());
425 // flNoLink - If file is a symlink then deref it /*{{{*/
426 // ---------------------------------------------------------------------
427 /* If the name is not a link then the returned path is the input. */
428 string
flNoLink(string File
)
431 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
433 if (stat(File
.c_str(),&St
) != 0)
436 /* Loop resolving the link. There is no need to limit the number of
437 loops because the stat call above ensures that the symlink is not
445 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
446 (unsigned)Res
>= sizeof(Buffer
))
449 // Append or replace the previous path
451 if (Buffer
[0] == '/')
454 NFile
= flNotFile(NFile
) + Buffer
;
456 // See if we are done
457 if (lstat(NFile
.c_str(),&St
) != 0)
459 if (S_ISLNK(St
.st_mode
) == 0)
464 // flCombine - Combine a file and a directory /*{{{*/
465 // ---------------------------------------------------------------------
466 /* If the file is an absolute path then it is just returned, otherwise
467 the directory is pre-pended to it. */
468 string
flCombine(string Dir
,string File
)
470 if (File
.empty() == true)
473 if (File
[0] == '/' || Dir
.empty() == true)
475 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
477 if (Dir
[Dir
.length()-1] == '/')
479 return Dir
+ '/' + File
;
482 // SetCloseExec - Set the close on exec flag /*{{{*/
483 // ---------------------------------------------------------------------
485 void SetCloseExec(int Fd
,bool Close
)
487 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
489 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
494 // SetNonBlock - Set the nonblocking flag /*{{{*/
495 // ---------------------------------------------------------------------
497 void SetNonBlock(int Fd
,bool Block
)
499 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
500 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
502 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
507 // WaitFd - Wait for a FD to become readable /*{{{*/
508 // ---------------------------------------------------------------------
509 /* This waits for a FD to become readable using select. It is useful for
510 applications making use of non-blocking sockets. The timeout is
512 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
525 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
527 while (Res
< 0 && errno
== EINTR
);
537 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
539 while (Res
< 0 && errno
== EINTR
);
548 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
549 // ---------------------------------------------------------------------
550 /* This is used if you want to cleanse the environment for the forked
551 child, it fixes up the important signals and nukes all of the fds,
552 otherwise acts like normal fork. */
555 // Fork off the process
556 pid_t Process
= fork();
559 cerr
<< "FATAL -> Failed to fork." << endl
;
563 // Spawn the subprocess
567 signal(SIGPIPE
,SIG_DFL
);
568 signal(SIGQUIT
,SIG_DFL
);
569 signal(SIGINT
,SIG_DFL
);
570 signal(SIGWINCH
,SIG_DFL
);
571 signal(SIGCONT
,SIG_DFL
);
572 signal(SIGTSTP
,SIG_DFL
);
575 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
576 if (Opts
!= 0 && Opts
->Child
!= 0)
579 for (; Opts
!= 0; Opts
= Opts
->Next
)
581 if (Opts
->Value
.empty() == true)
583 int fd
= atoi(Opts
->Value
.c_str());
588 // Close all of our FDs - just in case
589 for (int K
= 3; K
!= 40; K
++)
591 if(KeepFDs
.find(K
) == KeepFDs
.end())
592 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
599 // ExecWait - Fancy waitpid /*{{{*/
600 // ---------------------------------------------------------------------
601 /* Waits for the given sub process. If Reap is set then no errors are
602 generated. Otherwise a failed subprocess will generate a proper descriptive
604 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
609 // Wait and collect the error code
611 while (waitpid(Pid
,&Status
,0) != Pid
)
619 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
623 // Check for an error code.
624 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
628 if (WIFSIGNALED(Status
) != 0)
630 if( WTERMSIG(Status
) == SIGSEGV
)
631 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
633 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
636 if (WIFEXITED(Status
) != 0)
637 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
639 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
646 // FileFd::Open - Open a file /*{{{*/
647 // ---------------------------------------------------------------------
648 /* The most commonly used open mode combinations are given with Mode */
649 bool FileFd::Open(string FileName
,OpenMode Mode
, unsigned long Perms
)
656 iFd
= open(FileName
.c_str(),O_RDONLY
);
662 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
663 unlink(FileName
.c_str());
664 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_TRUNC
,Perms
);
669 iFd
= open(FileName
.c_str(),O_RDWR
);
673 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
,Perms
);
677 unlink(FileName
.c_str());
678 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_EXCL
,Perms
);
683 return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str());
685 this->FileName
= FileName
;
686 SetCloseExec(iFd
,true);
690 // FileFd::~File - Closes the file /*{{{*/
691 // ---------------------------------------------------------------------
692 /* If the proper modes are selected then we close the Fd and possibly
693 unlink the file on error. */
699 // FileFd::Read - Read a bit of the file /*{{{*/
700 // ---------------------------------------------------------------------
701 /* We are carefull to handle interruption by a signal while reading
703 bool FileFd::Read(void *To
,unsigned long Size
,unsigned long *Actual
)
712 Res
= read(iFd
,To
,Size
);
713 if (Res
< 0 && errno
== EINTR
)
718 return _error
->Errno("read",_("Read error"));
721 To
= (char *)To
+ Res
;
726 while (Res
> 0 && Size
> 0);
739 return _error
->Error(_("read, still have %lu to read but none left"),Size
);
742 // FileFd::Write - Write to the file /*{{{*/
743 // ---------------------------------------------------------------------
745 bool FileFd::Write(const void *From
,unsigned long Size
)
751 Res
= write(iFd
,From
,Size
);
752 if (Res
< 0 && errno
== EINTR
)
757 return _error
->Errno("write",_("Write error"));
760 From
= (char *)From
+ Res
;
763 while (Res
> 0 && Size
> 0);
769 return _error
->Error(_("write, still have %lu to write but couldn't"),Size
);
772 // FileFd::Seek - Seek in the file /*{{{*/
773 // ---------------------------------------------------------------------
775 bool FileFd::Seek(unsigned long To
)
777 if (lseek(iFd
,To
,SEEK_SET
) != (signed)To
)
780 return _error
->Error("Unable to seek to %lu",To
);
786 // FileFd::Skip - Seek in the file /*{{{*/
787 // ---------------------------------------------------------------------
789 bool FileFd::Skip(unsigned long Over
)
791 if (lseek(iFd
,Over
,SEEK_CUR
) < 0)
794 return _error
->Error("Unable to seek ahead %lu",Over
);
800 // FileFd::Truncate - Truncate the file /*{{{*/
801 // ---------------------------------------------------------------------
803 bool FileFd::Truncate(unsigned long To
)
805 if (ftruncate(iFd
,To
) != 0)
808 return _error
->Error("Unable to truncate to %lu",To
);
814 // FileFd::Tell - Current seek position /*{{{*/
815 // ---------------------------------------------------------------------
817 unsigned long FileFd::Tell()
819 off_t Res
= lseek(iFd
,0,SEEK_CUR
);
820 if (Res
== (off_t
)-1)
821 _error
->Errno("lseek","Failed to determine the current file position");
825 // FileFd::Size - Return the size of the file /*{{{*/
826 // ---------------------------------------------------------------------
828 unsigned long FileFd::Size()
831 if (fstat(iFd
,&Buf
) != 0)
832 return _error
->Errno("fstat","Unable to determine the file size");
836 // FileFd::Close - Close the file if the close flag is set /*{{{*/
837 // ---------------------------------------------------------------------
842 if ((Flags
& AutoClose
) == AutoClose
)
843 if (iFd
>= 0 && close(iFd
) != 0)
844 Res
&= _error
->Errno("close",_("Problem closing the file"));
847 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
848 FileName
.empty() == false)
849 if (unlink(FileName
.c_str()) != 0)
850 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file"));
854 // FileFd::Sync - Sync the file /*{{{*/
855 // ---------------------------------------------------------------------
859 #ifdef _POSIX_SYNCHRONIZED_IO
861 return _error
->Errno("sync",_("Problem syncing the file"));