]>
git.saurik.com Git - apt-legacy.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/error.h>
22 #include <apt-pkg/sptr.h>
23 #include <apt-pkg/configuration.h>
34 #include <sys/types.h>
46 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
47 // ---------------------------------------------------------------------
49 bool RunScripts(const char *Cnf
)
51 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
52 if (Opts
== 0 || Opts
->Child
== 0)
56 // Fork for running the system calls
57 pid_t Child
= ExecFork();
62 if (chdir("/tmp/") != 0)
65 unsigned int Count
= 1;
66 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
68 if (Opts
->Value
.empty() == true)
71 if (system(Opts
->Value
.c_str()) != 0)
79 while (waitpid(Child
,&Status
,0) != Child
)
83 return _error
->Errno("waitpid","Couldn't wait for subprocess");
86 // Restore sig int/quit
87 signal(SIGQUIT
,SIG_DFL
);
88 signal(SIGINT
,SIG_DFL
);
90 // Check for an error code.
91 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
93 unsigned int Count
= WEXITSTATUS(Status
);
97 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
98 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
101 return _error
->Error("Sub-process returned an error code");
108 // CopyFile - Buffered copy of a file /*{{{*/
109 // ---------------------------------------------------------------------
110 /* The caller is expected to set things so that failure causes erasure */
111 bool CopyFile(FileFd
&From
,FileFd
&To
)
113 if (From
.IsOpen() == false || To
.IsOpen() == false)
116 // Buffered copy between fds
117 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
118 unsigned long Size
= From
.Size();
121 unsigned long ToRead
= Size
;
125 if (From
.Read(Buf
,ToRead
) == false ||
126 To
.Write(Buf
,ToRead
) == false)
135 // GetLock - Gets a lock file /*{{{*/
136 // ---------------------------------------------------------------------
137 /* This will create an empty file of the given name and lock it. Once this
138 is done all other calls to GetLock in any other process will fail with
139 -1. The return result is the fd of the file, the call should call
140 close at some time. */
141 int GetLock(string File
,bool Errors
)
143 // GetLock() is used in aptitude on directories with public-write access
144 // Use O_NOFOLLOW here to prevent symlink traversal attacks
145 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
148 // Read only .. cant have locking problems there.
151 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
152 return dup(0); // Need something for the caller to close
156 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
158 // Feh.. We do this to distinguish the lock vs open case..
162 SetCloseExec(FD
,true);
164 // Aquire a write lock
167 fl
.l_whence
= SEEK_SET
;
170 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
174 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
175 return dup(0); // Need something for the caller to close
178 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
189 // FileExists - Check if a file exists /*{{{*/
190 // ---------------------------------------------------------------------
192 bool FileExists(string File
)
195 if (stat(File
.c_str(),&Buf
) != 0)
200 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
201 // ---------------------------------------------------------------------
202 /* If an extension is given only files with this extension are included
203 in the returned vector, otherwise every "normal" file is included. */
204 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
205 bool const &SortList
)
207 return GetListOfFilesInDir(Dir
, Ext
, SortList
, false);
209 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
210 bool const &SortList
, bool const &AllowNoExt
)
212 std::vector
<string
> ext
;
214 if (Ext
.empty() == false)
216 if (AllowNoExt
== true && ext
.empty() == false)
218 return GetListOfFilesInDir(Dir
, ext
, SortList
);
220 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
221 bool const &SortList
)
223 // Attention debuggers: need to be set with the environment config file!
224 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
227 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
228 if (Ext
.empty() == true)
229 std::clog
<< "\tNO extension" << std::endl
;
231 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
233 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
236 std::vector
<string
> List
;
237 DIR *D
= opendir(Dir
.c_str());
240 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
244 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
246 // skip "hidden" files
247 if (Ent
->d_name
[0] == '.')
250 // check for accepted extension:
251 // no extension given -> periods are bad as hell!
252 // extensions given -> "" extension allows no extension
253 if (Ext
.empty() == false)
255 string d_ext
= flExtension(Ent
->d_name
);
256 if (d_ext
== Ent
->d_name
) // no extension
258 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
261 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
265 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
268 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
273 // Skip bad filenames ala run-parts
274 const char *C
= Ent
->d_name
;
276 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
277 && *C
!= '_' && *C
!= '-') {
278 // no required extension -> dot is a bad character
279 if (*C
== '.' && Ext
.empty() == false)
284 // we don't reach the end of the name -> bad character included
288 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
289 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
293 // skip filenames which end with a period. These are never valid
297 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
301 // Make sure it is a file and not something else
302 string
const File
= flCombine(Dir
,Ent
->d_name
);
304 if (stat(File
.c_str(),&St
) != 0 || S_ISREG(St
.st_mode
) == 0)
307 std::clog
<< "Bad file: " << Ent
->d_name
<< " → stat says not a good file" << std::endl
;
312 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
313 List
.push_back(File
);
317 if (SortList
== true)
318 std::sort(List
.begin(),List
.end());
322 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
323 // ---------------------------------------------------------------------
324 /* We return / on failure. */
327 // Stash the current dir.
330 if (getcwd(S
,sizeof(S
)-2) == 0)
332 unsigned int Len
= strlen(S
);
338 // flNotDir - Strip the directory from the filename /*{{{*/
339 // ---------------------------------------------------------------------
341 string
flNotDir(string File
)
343 string::size_type Res
= File
.rfind('/');
344 if (Res
== string::npos
)
347 return string(File
,Res
,Res
- File
.length());
350 // flNotFile - Strip the file from the directory name /*{{{*/
351 // ---------------------------------------------------------------------
352 /* Result ends in a / */
353 string
flNotFile(string File
)
355 string::size_type Res
= File
.rfind('/');
356 if (Res
== string::npos
)
359 return string(File
,0,Res
);
362 // flExtension - Return the extension for the file /*{{{*/
363 // ---------------------------------------------------------------------
365 string
flExtension(string File
)
367 string::size_type Res
= File
.rfind('.');
368 if (Res
== string::npos
)
371 return string(File
,Res
,Res
- File
.length());
374 // flNoLink - If file is a symlink then deref it /*{{{*/
375 // ---------------------------------------------------------------------
376 /* If the name is not a link then the returned path is the input. */
377 string
flNoLink(string File
)
380 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
382 if (stat(File
.c_str(),&St
) != 0)
385 /* Loop resolving the link. There is no need to limit the number of
386 loops because the stat call above ensures that the symlink is not
394 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
395 (unsigned)Res
>= sizeof(Buffer
))
398 // Append or replace the previous path
400 if (Buffer
[0] == '/')
403 NFile
= flNotFile(NFile
) + Buffer
;
405 // See if we are done
406 if (lstat(NFile
.c_str(),&St
) != 0)
408 if (S_ISLNK(St
.st_mode
) == 0)
413 // flCombine - Combine a file and a directory /*{{{*/
414 // ---------------------------------------------------------------------
415 /* If the file is an absolute path then it is just returned, otherwise
416 the directory is pre-pended to it. */
417 string
flCombine(string Dir
,string File
)
419 if (File
.empty() == true)
422 if (File
[0] == '/' || Dir
.empty() == true)
424 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
426 if (Dir
[Dir
.length()-1] == '/')
428 return Dir
+ '/' + File
;
431 // SetCloseExec - Set the close on exec flag /*{{{*/
432 // ---------------------------------------------------------------------
434 void SetCloseExec(int Fd
,bool Close
)
436 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
438 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
443 // SetNonBlock - Set the nonblocking flag /*{{{*/
444 // ---------------------------------------------------------------------
446 void SetNonBlock(int Fd
,bool Block
)
448 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
449 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
451 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
456 // WaitFd - Wait for a FD to become readable /*{{{*/
457 // ---------------------------------------------------------------------
458 /* This waits for a FD to become readable using select. It is useful for
459 applications making use of non-blocking sockets. The timeout is
461 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
474 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
476 while (Res
< 0 && errno
== EINTR
);
486 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
488 while (Res
< 0 && errno
== EINTR
);
497 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
498 // ---------------------------------------------------------------------
499 /* This is used if you want to cleanse the environment for the forked
500 child, it fixes up the important signals and nukes all of the fds,
501 otherwise acts like normal fork. */
504 // Fork off the process
505 pid_t Process
= fork();
508 cerr
<< "FATAL -> Failed to fork." << endl
;
512 // Spawn the subprocess
516 signal(SIGPIPE
,SIG_DFL
);
517 signal(SIGQUIT
,SIG_DFL
);
518 signal(SIGINT
,SIG_DFL
);
519 signal(SIGWINCH
,SIG_DFL
);
520 signal(SIGCONT
,SIG_DFL
);
521 signal(SIGTSTP
,SIG_DFL
);
524 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
525 if (Opts
!= 0 && Opts
->Child
!= 0)
528 for (; Opts
!= 0; Opts
= Opts
->Next
)
530 if (Opts
->Value
.empty() == true)
532 int fd
= atoi(Opts
->Value
.c_str());
537 // Close all of our FDs - just in case
538 for (int K
= 3; K
!= 40; K
++)
540 if(KeepFDs
.find(K
) == KeepFDs
.end())
541 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
548 // ExecWait - Fancy waitpid /*{{{*/
549 // ---------------------------------------------------------------------
550 /* Waits for the given sub process. If Reap is set then no errors are
551 generated. Otherwise a failed subprocess will generate a proper descriptive
553 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
558 // Wait and collect the error code
560 while (waitpid(Pid
,&Status
,0) != Pid
)
568 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
572 // Check for an error code.
573 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
577 if (WIFSIGNALED(Status
) != 0)
579 if( WTERMSIG(Status
) == SIGSEGV
)
580 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
582 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
585 if (WIFEXITED(Status
) != 0)
586 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
588 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
595 // FileFd::Open - Open a file /*{{{*/
596 // ---------------------------------------------------------------------
597 /* The most commonly used open mode combinations are given with Mode */
598 bool FileFd::Open(string FileName
,OpenMode Mode
, unsigned long Perms
)
605 iFd
= open(FileName
.c_str(),O_RDONLY
);
611 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
612 unlink(FileName
.c_str());
613 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_TRUNC
,Perms
);
618 iFd
= open(FileName
.c_str(),O_RDWR
);
622 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
,Perms
);
626 unlink(FileName
.c_str());
627 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_EXCL
,Perms
);
632 return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str());
634 this->FileName
= FileName
;
635 SetCloseExec(iFd
,true);
639 // FileFd::~File - Closes the file /*{{{*/
640 // ---------------------------------------------------------------------
641 /* If the proper modes are selected then we close the Fd and possibly
642 unlink the file on error. */
648 // FileFd::Read - Read a bit of the file /*{{{*/
649 // ---------------------------------------------------------------------
650 /* We are carefull to handle interruption by a signal while reading
652 bool FileFd::Read(void *To
,unsigned long Size
,unsigned long *Actual
)
661 Res
= read(iFd
,To
,Size
);
662 if (Res
< 0 && errno
== EINTR
)
667 return _error
->Errno("read",_("Read error"));
670 To
= (char *)To
+ Res
;
675 while (Res
> 0 && Size
> 0);
688 return _error
->Error(_("read, still have %lu to read but none left"),Size
);
691 // FileFd::Write - Write to the file /*{{{*/
692 // ---------------------------------------------------------------------
694 bool FileFd::Write(const void *From
,unsigned long Size
)
700 Res
= write(iFd
,From
,Size
);
701 if (Res
< 0 && errno
== EINTR
)
706 return _error
->Errno("write",_("Write error"));
709 From
= (char *)From
+ Res
;
712 while (Res
> 0 && Size
> 0);
718 return _error
->Error(_("write, still have %lu to write but couldn't"),Size
);
721 // FileFd::Seek - Seek in the file /*{{{*/
722 // ---------------------------------------------------------------------
724 bool FileFd::Seek(unsigned long To
)
726 if (lseek(iFd
,To
,SEEK_SET
) != (signed)To
)
729 return _error
->Error("Unable to seek to %lu",To
);
735 // FileFd::Skip - Seek in the file /*{{{*/
736 // ---------------------------------------------------------------------
738 bool FileFd::Skip(unsigned long Over
)
740 if (lseek(iFd
,Over
,SEEK_CUR
) < 0)
743 return _error
->Error("Unable to seek ahead %lu",Over
);
749 // FileFd::Truncate - Truncate the file /*{{{*/
750 // ---------------------------------------------------------------------
752 bool FileFd::Truncate(unsigned long To
)
754 if (ftruncate(iFd
,To
) != 0)
757 return _error
->Error("Unable to truncate to %lu",To
);
763 // FileFd::Tell - Current seek position /*{{{*/
764 // ---------------------------------------------------------------------
766 unsigned long FileFd::Tell()
768 off_t Res
= lseek(iFd
,0,SEEK_CUR
);
769 if (Res
== (off_t
)-1)
770 _error
->Errno("lseek","Failed to determine the current file position");
774 // FileFd::Size - Return the size of the file /*{{{*/
775 // ---------------------------------------------------------------------
777 unsigned long FileFd::Size()
780 if (fstat(iFd
,&Buf
) != 0)
781 return _error
->Errno("fstat","Unable to determine the file size");
785 // FileFd::Close - Close the file if the close flag is set /*{{{*/
786 // ---------------------------------------------------------------------
791 if ((Flags
& AutoClose
) == AutoClose
)
792 if (iFd
>= 0 && close(iFd
) != 0)
793 Res
&= _error
->Errno("close",_("Problem closing the file"));
796 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
797 FileName
.empty() == false)
798 if (unlink(FileName
.c_str()) != 0)
799 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file"));
803 // FileFd::Sync - Sync the file /*{{{*/
804 // ---------------------------------------------------------------------
808 #ifdef _POSIX_SYNCHRONIZED_IO
810 return _error
->Errno("sync",_("Problem syncing the file"));