]>
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/error.h>
22 #include <apt-pkg/sptr.h>
23 #include <apt-pkg/configuration.h>
34 #include <sys/types.h>
44 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
45 // ---------------------------------------------------------------------
47 bool RunScripts(const char *Cnf
)
49 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
50 if (Opts
== 0 || Opts
->Child
== 0)
54 // Fork for running the system calls
55 pid_t Child
= ExecFork();
60 if (chdir("/tmp/") != 0)
63 unsigned int Count
= 1;
64 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
66 if (Opts
->Value
.empty() == true)
69 if (system(Opts
->Value
.c_str()) != 0)
77 while (waitpid(Child
,&Status
,0) != Child
)
81 return _error
->Errno("waitpid","Couldn't wait for subprocess");
84 // Restore sig int/quit
85 signal(SIGQUIT
,SIG_DFL
);
86 signal(SIGINT
,SIG_DFL
);
88 // Check for an error code.
89 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
91 unsigned int Count
= WEXITSTATUS(Status
);
95 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
96 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
99 return _error
->Error("Sub-process returned an error code");
106 // CopyFile - Buffered copy of a file /*{{{*/
107 // ---------------------------------------------------------------------
108 /* The caller is expected to set things so that failure causes erasure */
109 bool CopyFile(FileFd
&From
,FileFd
&To
)
111 if (From
.IsOpen() == false || To
.IsOpen() == false)
114 // Buffered copy between fds
115 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
116 unsigned long Size
= From
.Size();
119 unsigned long ToRead
= Size
;
123 if (From
.Read(Buf
,ToRead
) == false ||
124 To
.Write(Buf
,ToRead
) == false)
133 // GetLock - Gets a lock file /*{{{*/
134 // ---------------------------------------------------------------------
135 /* This will create an empty file of the given name and lock it. Once this
136 is done all other calls to GetLock in any other process will fail with
137 -1. The return result is the fd of the file, the call should call
138 close at some time. */
139 int GetLock(string File
,bool Errors
)
141 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_TRUNC
,0640);
144 // Read only .. cant have locking problems there.
147 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
148 return dup(0); // Need something for the caller to close
152 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
154 // Feh.. We do this to distinguish the lock vs open case..
158 SetCloseExec(FD
,true);
160 // Aquire a write lock
163 fl
.l_whence
= SEEK_SET
;
166 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
170 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
171 return dup(0); // Need something for the caller to close
174 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
185 // FileExists - Check if a file exists /*{{{*/
186 // ---------------------------------------------------------------------
188 bool FileExists(string File
)
191 if (stat(File
.c_str(),&Buf
) != 0)
196 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
197 // ---------------------------------------------------------------------
198 /* We return / on failure. */
201 // Stash the current dir.
204 if (getcwd(S
,sizeof(S
)-2) == 0)
206 unsigned int Len
= strlen(S
);
212 // flNotDir - Strip the directory from the filename /*{{{*/
213 // ---------------------------------------------------------------------
215 string
flNotDir(string File
)
217 string::size_type Res
= File
.rfind('/');
218 if (Res
== string::npos
)
221 return string(File
,Res
,Res
- File
.length());
224 // flNotFile - Strip the file from the directory name /*{{{*/
225 // ---------------------------------------------------------------------
226 /* Result ends in a / */
227 string
flNotFile(string File
)
229 string::size_type Res
= File
.rfind('/');
230 if (Res
== string::npos
)
233 return string(File
,0,Res
);
236 // flExtension - Return the extension for the file /*{{{*/
237 // ---------------------------------------------------------------------
239 string
flExtension(string File
)
241 string::size_type Res
= File
.rfind('.');
242 if (Res
== string::npos
)
245 return string(File
,Res
,Res
- File
.length());
248 // flNoLink - If file is a symlink then deref it /*{{{*/
249 // ---------------------------------------------------------------------
250 /* If the name is not a link then the returned path is the input. */
251 string
flNoLink(string File
)
254 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
256 if (stat(File
.c_str(),&St
) != 0)
259 /* Loop resolving the link. There is no need to limit the number of
260 loops because the stat call above ensures that the symlink is not
268 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
269 (unsigned)Res
>= sizeof(Buffer
))
272 // Append or replace the previous path
274 if (Buffer
[0] == '/')
277 NFile
= flNotFile(NFile
) + Buffer
;
279 // See if we are done
280 if (lstat(NFile
.c_str(),&St
) != 0)
282 if (S_ISLNK(St
.st_mode
) == 0)
287 // flCombine - Combine a file and a directory /*{{{*/
288 // ---------------------------------------------------------------------
289 /* If the file is an absolute path then it is just returned, otherwise
290 the directory is pre-pended to it. */
291 string
flCombine(string Dir
,string File
)
293 if (File
.empty() == true)
296 if (File
[0] == '/' || Dir
.empty() == true)
298 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
300 if (Dir
[Dir
.length()-1] == '/')
302 return Dir
+ '/' + File
;
305 // SetCloseExec - Set the close on exec flag /*{{{*/
306 // ---------------------------------------------------------------------
308 void SetCloseExec(int Fd
,bool Close
)
310 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
312 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
317 // SetNonBlock - Set the nonblocking flag /*{{{*/
318 // ---------------------------------------------------------------------
320 void SetNonBlock(int Fd
,bool Block
)
322 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
323 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
325 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
330 // WaitFd - Wait for a FD to become readable /*{{{*/
331 // ---------------------------------------------------------------------
332 /* This waits for a FD to become readable using select. It is useful for
333 applications making use of non-blocking sockets. The timeout is
335 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
348 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
350 while (Res
< 0 && errno
== EINTR
);
360 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
362 while (Res
< 0 && errno
== EINTR
);
371 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
372 // ---------------------------------------------------------------------
373 /* This is used if you want to cleanse the environment for the forked
374 child, it fixes up the important signals and nukes all of the fds,
375 otherwise acts like normal fork. */
378 // Fork off the process
379 pid_t Process
= fork();
382 cerr
<< "FATAL -> Failed to fork." << endl
;
386 // Spawn the subprocess
390 signal(SIGPIPE
,SIG_DFL
);
391 signal(SIGQUIT
,SIG_DFL
);
392 signal(SIGINT
,SIG_DFL
);
393 signal(SIGWINCH
,SIG_DFL
);
394 signal(SIGCONT
,SIG_DFL
);
395 signal(SIGTSTP
,SIG_DFL
);
398 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
399 if (Opts
!= 0 && Opts
->Child
!= 0)
402 for (; Opts
!= 0; Opts
= Opts
->Next
)
404 if (Opts
->Value
.empty() == true)
406 int fd
= atoi(Opts
->Value
.c_str());
411 // Close all of our FDs - just in case
412 for (int K
= 3; K
!= 40; K
++)
414 if(KeepFDs
.find(K
) == KeepFDs
.end())
415 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
422 // ExecWait - Fancy waitpid /*{{{*/
423 // ---------------------------------------------------------------------
424 /* Waits for the given sub process. If Reap is set then no errors are
425 generated. Otherwise a failed subprocess will generate a proper descriptive
427 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
432 // Wait and collect the error code
434 while (waitpid(Pid
,&Status
,0) != Pid
)
442 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
446 // Check for an error code.
447 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
451 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
452 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
454 if (WIFEXITED(Status
) != 0)
455 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
457 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
464 // FileFd::Open - Open a file /*{{{*/
465 // ---------------------------------------------------------------------
466 /* The most commonly used open mode combinations are given with Mode */
467 bool FileFd::Open(string FileName
,OpenMode Mode
, unsigned long Perms
)
474 iFd
= open(FileName
.c_str(),O_RDONLY
);
480 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
481 unlink(FileName
.c_str());
482 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_TRUNC
,Perms
);
487 iFd
= open(FileName
.c_str(),O_RDWR
);
491 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
,Perms
);
495 unlink(FileName
.c_str());
496 iFd
= open(FileName
.c_str(),O_RDWR
| O_CREAT
| O_EXCL
,Perms
);
501 return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str());
503 this->FileName
= FileName
;
504 SetCloseExec(iFd
,true);
508 // FileFd::~File - Closes the file /*{{{*/
509 // ---------------------------------------------------------------------
510 /* If the proper modes are selected then we close the Fd and possibly
511 unlink the file on error. */
517 // FileFd::Read - Read a bit of the file /*{{{*/
518 // ---------------------------------------------------------------------
519 /* We are carefull to handle interruption by a signal while reading
521 bool FileFd::Read(void *To
,unsigned long Size
,unsigned long *Actual
)
530 Res
= read(iFd
,To
,Size
);
531 if (Res
< 0 && errno
== EINTR
)
536 return _error
->Errno("read",_("Read error"));
539 To
= (char *)To
+ Res
;
544 while (Res
> 0 && Size
> 0);
557 return _error
->Error(_("read, still have %lu to read but none left"),Size
);
560 // FileFd::Write - Write to the file /*{{{*/
561 // ---------------------------------------------------------------------
563 bool FileFd::Write(const void *From
,unsigned long Size
)
569 Res
= write(iFd
,From
,Size
);
570 if (Res
< 0 && errno
== EINTR
)
575 return _error
->Errno("write",_("Write error"));
578 From
= (char *)From
+ Res
;
581 while (Res
> 0 && Size
> 0);
587 return _error
->Error(_("write, still have %lu to write but couldn't"),Size
);
590 // FileFd::Seek - Seek in the file /*{{{*/
591 // ---------------------------------------------------------------------
593 bool FileFd::Seek(unsigned long To
)
595 if (lseek(iFd
,To
,SEEK_SET
) != (signed)To
)
598 return _error
->Error("Unable to seek to %lu",To
);
604 // FileFd::Skip - Seek in the file /*{{{*/
605 // ---------------------------------------------------------------------
607 bool FileFd::Skip(unsigned long Over
)
609 if (lseek(iFd
,Over
,SEEK_CUR
) < 0)
612 return _error
->Error("Unable to seek ahead %lu",Over
);
618 // FileFd::Truncate - Truncate the file /*{{{*/
619 // ---------------------------------------------------------------------
621 bool FileFd::Truncate(unsigned long To
)
623 if (ftruncate(iFd
,To
) != 0)
626 return _error
->Error("Unable to truncate to %lu",To
);
632 // FileFd::Tell - Current seek position /*{{{*/
633 // ---------------------------------------------------------------------
635 unsigned long FileFd::Tell()
637 off_t Res
= lseek(iFd
,0,SEEK_CUR
);
638 if (Res
== (off_t
)-1)
639 _error
->Errno("lseek","Failed to determine the current file position");
643 // FileFd::Size - Return the size of the file /*{{{*/
644 // ---------------------------------------------------------------------
646 unsigned long FileFd::Size()
649 if (fstat(iFd
,&Buf
) != 0)
650 return _error
->Errno("fstat","Unable to determine the file size");
654 // FileFd::Close - Close the file if the close flag is set /*{{{*/
655 // ---------------------------------------------------------------------
660 if ((Flags
& AutoClose
) == AutoClose
)
661 if (iFd
>= 0 && close(iFd
) != 0)
662 Res
&= _error
->Errno("close",_("Problem closing the file"));
665 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
666 FileName
.empty() == false)
667 if (unlink(FileName
.c_str()) != 0)
668 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file"));
672 // FileFd::Sync - Sync the file /*{{{*/
673 // ---------------------------------------------------------------------
677 #ifdef _POSIX_SYNCHRONIZED_IO
679 return _error
->Errno("sync",_("Problem syncing the file"));