]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
* add "purge" commandline argument, closes: #133421)
[apt.git] / apt-pkg / contrib / fileutl.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7da2b375 3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
11 This source is placed in the Public Domain, do with it what you will
7da2b375 12 It was originally written by Jason Gunthorpe <jgg@debian.org>.
578bfd0a
AL
13
14 ##################################################################### */
15 /*}}}*/
16// Include Files /*{{{*/
6c139d6e 17#ifdef __GNUG__
094a497d 18#pragma implementation "apt-pkg/fileutl.h"
6c139d6e 19#endif
094a497d
AL
20#include <apt-pkg/fileutl.h>
21#include <apt-pkg/error.h>
b2e465d6 22#include <apt-pkg/sptr.h>
75ef8f14 23#include <apt-pkg/configuration.h>
b2e465d6
AL
24
25#include <apti18n.h>
578bfd0a 26
152ab79e 27#include <cstdlib>
4d055c05 28#include <iostream>
578bfd0a 29#include <unistd.h>
2c206aa4 30#include <fcntl.h>
578bfd0a 31#include <sys/stat.h>
578bfd0a 32#include <sys/types.h>
cc2313b7 33#include <sys/time.h>
1ae93c94 34#include <sys/wait.h>
54676e1a 35#include <signal.h>
65a1e968 36#include <errno.h>
75ef8f14 37#include <set>
578bfd0a
AL
38 /*}}}*/
39
4d055c05
AL
40using namespace std;
41
578bfd0a
AL
42// CopyFile - Buffered copy of a file /*{{{*/
43// ---------------------------------------------------------------------
44/* The caller is expected to set things so that failure causes erasure */
8b89e57f 45bool CopyFile(FileFd &From,FileFd &To)
578bfd0a
AL
46{
47 if (From.IsOpen() == false || To.IsOpen() == false)
48 return false;
49
50 // Buffered copy between fds
b2e465d6 51 SPtrArray<unsigned char> Buf = new unsigned char[64000];
b0db36b1
AL
52 unsigned long Size = From.Size();
53 while (Size != 0)
578bfd0a 54 {
b0db36b1
AL
55 unsigned long ToRead = Size;
56 if (Size > 64000)
57 ToRead = 64000;
58
4a6d5862 59 if (From.Read(Buf,ToRead) == false ||
b0db36b1 60 To.Write(Buf,ToRead) == false)
578bfd0a 61 return false;
b0db36b1
AL
62
63 Size -= ToRead;
578bfd0a
AL
64 }
65
578bfd0a
AL
66 return true;
67}
68 /*}}}*/
69// GetLock - Gets a lock file /*{{{*/
70// ---------------------------------------------------------------------
71/* This will create an empty file of the given name and lock it. Once this
72 is done all other calls to GetLock in any other process will fail with
73 -1. The return result is the fd of the file, the call should call
74 close at some time. */
75int GetLock(string File,bool Errors)
76{
77 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_TRUNC,0640);
78 if (FD < 0)
79 {
b2e465d6
AL
80 // Read only .. cant have locking problems there.
81 if (errno == EROFS)
82 {
83 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
84 return dup(0); // Need something for the caller to close
85 }
86
578bfd0a 87 if (Errors == true)
b2e465d6
AL
88 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
89
90 // Feh.. We do this to distinguish the lock vs open case..
91 errno = EPERM;
578bfd0a
AL
92 return -1;
93 }
b2e465d6
AL
94 SetCloseExec(FD,true);
95
578bfd0a
AL
96 // Aquire a write lock
97 struct flock fl;
c71bc556
AL
98 fl.l_type = F_WRLCK;
99 fl.l_whence = SEEK_SET;
100 fl.l_start = 0;
101 fl.l_len = 0;
578bfd0a
AL
102 if (fcntl(FD,F_SETLK,&fl) == -1)
103 {
d89df07a
AL
104 if (errno == ENOLCK)
105 {
b2e465d6
AL
106 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
107 return dup(0); // Need something for the caller to close
d89df07a 108 }
578bfd0a 109 if (Errors == true)
b2e465d6
AL
110 _error->Errno("open",_("Could not get lock %s"),File.c_str());
111
112 int Tmp = errno;
578bfd0a 113 close(FD);
b2e465d6 114 errno = Tmp;
578bfd0a
AL
115 return -1;
116 }
117
118 return FD;
119}
120 /*}}}*/
121// FileExists - Check if a file exists /*{{{*/
122// ---------------------------------------------------------------------
123/* */
124bool FileExists(string File)
125{
126 struct stat Buf;
127 if (stat(File.c_str(),&Buf) != 0)
128 return false;
129 return true;
130}
131 /*}}}*/
132// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
133// ---------------------------------------------------------------------
134/* We return / on failure. */
135string SafeGetCWD()
136{
137 // Stash the current dir.
138 char S[300];
139 S[0] = 0;
7f25bdff 140 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 141 return "/";
7f25bdff
AL
142 unsigned int Len = strlen(S);
143 S[Len] = '/';
144 S[Len+1] = 0;
578bfd0a
AL
145 return S;
146}
147 /*}}}*/
8ce4327b
AL
148// flNotDir - Strip the directory from the filename /*{{{*/
149// ---------------------------------------------------------------------
150/* */
151string flNotDir(string File)
152{
153 string::size_type Res = File.rfind('/');
154 if (Res == string::npos)
155 return File;
156 Res++;
157 return string(File,Res,Res - File.length());
158}
159 /*}}}*/
d38b7b3d
AL
160// flNotFile - Strip the file from the directory name /*{{{*/
161// ---------------------------------------------------------------------
171c45bc 162/* Result ends in a / */
d38b7b3d
AL
163string flNotFile(string File)
164{
165 string::size_type Res = File.rfind('/');
166 if (Res == string::npos)
171c45bc 167 return "./";
d38b7b3d
AL
168 Res++;
169 return string(File,0,Res);
170}
171 /*}}}*/
b2e465d6
AL
172// flExtension - Return the extension for the file /*{{{*/
173// ---------------------------------------------------------------------
174/* */
175string flExtension(string File)
176{
177 string::size_type Res = File.rfind('.');
178 if (Res == string::npos)
179 return File;
180 Res++;
181 return string(File,Res,Res - File.length());
182}
183 /*}}}*/
421c8d10
AL
184// flNoLink - If file is a symlink then deref it /*{{{*/
185// ---------------------------------------------------------------------
186/* If the name is not a link then the returned path is the input. */
187string flNoLink(string File)
188{
189 struct stat St;
190 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
191 return File;
192 if (stat(File.c_str(),&St) != 0)
193 return File;
194
195 /* Loop resolving the link. There is no need to limit the number of
196 loops because the stat call above ensures that the symlink is not
197 circular */
198 char Buffer[1024];
199 string NFile = File;
200 while (1)
201 {
202 // Read the link
203 int Res;
204 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
205 (unsigned)Res >= sizeof(Buffer))
206 return File;
207
208 // Append or replace the previous path
209 Buffer[Res] = 0;
210 if (Buffer[0] == '/')
211 NFile = Buffer;
212 else
213 NFile = flNotFile(NFile) + Buffer;
214
215 // See if we are done
216 if (lstat(NFile.c_str(),&St) != 0)
217 return File;
218 if (S_ISLNK(St.st_mode) == 0)
219 return NFile;
220 }
221}
222 /*}}}*/
b2e465d6
AL
223// flCombine - Combine a file and a directory /*{{{*/
224// ---------------------------------------------------------------------
225/* If the file is an absolute path then it is just returned, otherwise
226 the directory is pre-pended to it. */
227string flCombine(string Dir,string File)
228{
229 if (File.empty() == true)
230 return string();
231
232 if (File[0] == '/' || Dir.empty() == true)
233 return File;
234 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
235 return File;
236 if (Dir[Dir.length()-1] == '/')
237 return Dir + File;
238 return Dir + '/' + File;
239}
240 /*}}}*/
3b5421b4
AL
241// SetCloseExec - Set the close on exec flag /*{{{*/
242// ---------------------------------------------------------------------
243/* */
244void SetCloseExec(int Fd,bool Close)
245{
246 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
247 {
248 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
249 exit(100);
250 }
251}
252 /*}}}*/
253// SetNonBlock - Set the nonblocking flag /*{{{*/
254// ---------------------------------------------------------------------
255/* */
256void SetNonBlock(int Fd,bool Block)
257{
0a8a80e5
AL
258 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
259 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
260 {
261 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
262 exit(100);
263 }
264}
265 /*}}}*/
266// WaitFd - Wait for a FD to become readable /*{{{*/
267// ---------------------------------------------------------------------
b2e465d6 268/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
269 applications making use of non-blocking sockets. The timeout is
270 in seconds. */
1084d58a 271bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
272{
273 fd_set Set;
cc2313b7 274 struct timeval tv;
3b5421b4
AL
275 FD_ZERO(&Set);
276 FD_SET(Fd,&Set);
6d5dd02a
AL
277 tv.tv_sec = timeout;
278 tv.tv_usec = 0;
1084d58a 279 if (write == true)
b0db36b1
AL
280 {
281 int Res;
282 do
283 {
284 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
285 }
286 while (Res < 0 && errno == EINTR);
287
288 if (Res <= 0)
289 return false;
1084d58a
AL
290 }
291 else
292 {
b0db36b1
AL
293 int Res;
294 do
295 {
296 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
297 }
298 while (Res < 0 && errno == EINTR);
299
300 if (Res <= 0)
301 return false;
cc2313b7 302 }
1084d58a 303
3b5421b4
AL
304 return true;
305}
306 /*}}}*/
54676e1a
AL
307// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
308// ---------------------------------------------------------------------
309/* This is used if you want to cleanse the environment for the forked
310 child, it fixes up the important signals and nukes all of the fds,
311 otherwise acts like normal fork. */
75ef8f14 312pid_t ExecFork()
54676e1a
AL
313{
314 // Fork off the process
315 pid_t Process = fork();
316 if (Process < 0)
317 {
318 cerr << "FATAL -> Failed to fork." << endl;
319 exit(100);
320 }
321
322 // Spawn the subprocess
323 if (Process == 0)
324 {
325 // Setup the signals
326 signal(SIGPIPE,SIG_DFL);
327 signal(SIGQUIT,SIG_DFL);
328 signal(SIGINT,SIG_DFL);
329 signal(SIGWINCH,SIG_DFL);
330 signal(SIGCONT,SIG_DFL);
331 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
332
333 set<int> KeepFDs;
334 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
335 if (Opts != 0 && Opts->Child != 0)
336 {
337 Opts = Opts->Child;
338 for (; Opts != 0; Opts = Opts->Next)
339 {
340 if (Opts->Value.empty() == true)
341 continue;
342 int fd = atoi(Opts->Value.c_str());
343 KeepFDs.insert(fd);
344 }
345 }
346
54676e1a
AL
347 // Close all of our FDs - just in case
348 for (int K = 3; K != 40; K++)
75ef8f14
MV
349 {
350 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 351 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 352 }
54676e1a
AL
353 }
354
355 return Process;
356}
357 /*}}}*/
ddc1d8d0
AL
358// ExecWait - Fancy waitpid /*{{{*/
359// ---------------------------------------------------------------------
2c9a72d1 360/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
361 generated. Otherwise a failed subprocess will generate a proper descriptive
362 message */
3826564e 363bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
364{
365 if (Pid <= 1)
366 return true;
367
368 // Wait and collect the error code
369 int Status;
370 while (waitpid(Pid,&Status,0) != Pid)
371 {
372 if (errno == EINTR)
373 continue;
374
375 if (Reap == true)
376 return false;
377
db0db9fe 378 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
379 }
380
381
382 // Check for an error code.
383 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
384 {
385 if (Reap == true)
386 return false;
387 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
b2e465d6 388 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
ddc1d8d0
AL
389
390 if (WIFEXITED(Status) != 0)
b2e465d6 391 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 392
b2e465d6 393 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
394 }
395
396 return true;
397}
398 /*}}}*/
578bfd0a 399
13d87e2e 400// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
401// ---------------------------------------------------------------------
402/* The most commonly used open mode combinations are given with Mode */
13d87e2e 403bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 404{
13d87e2e 405 Close();
1164783d 406 Flags = AutoClose;
578bfd0a
AL
407 switch (Mode)
408 {
409 case ReadOnly:
410 iFd = open(FileName.c_str(),O_RDONLY);
411 break;
412
413 case WriteEmpty:
50b513a1
AL
414 {
415 struct stat Buf;
459681d3 416 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
50b513a1
AL
417 unlink(FileName.c_str());
418 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
419 break;
420 }
578bfd0a
AL
421
422 case WriteExists:
423 iFd = open(FileName.c_str(),O_RDWR);
424 break;
0a8e3465
AL
425
426 case WriteAny:
427 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 428 break;
f08fcf34
AL
429
430 case WriteTemp:
4decd43c
AL
431 unlink(FileName.c_str());
432 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 433 break;
578bfd0a
AL
434 }
435
436 if (iFd < 0)
b2e465d6 437 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
438
439 this->FileName = FileName;
440 SetCloseExec(iFd,true);
441 return true;
578bfd0a
AL
442}
443 /*}}}*/
8e06abb2 444// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
445// ---------------------------------------------------------------------
446/* If the proper modes are selected then we close the Fd and possibly
447 unlink the file on error. */
8e06abb2 448FileFd::~FileFd()
578bfd0a
AL
449{
450 Close();
451}
452 /*}}}*/
8e06abb2 453// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 454// ---------------------------------------------------------------------
b0db36b1
AL
455/* We are carefull to handle interruption by a signal while reading
456 gracefully. */
f604cf55 457bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
578bfd0a 458{
b0db36b1
AL
459 int Res;
460 errno = 0;
f604cf55
AL
461 if (Actual != 0)
462 *Actual = 0;
463
b0db36b1 464 do
578bfd0a 465 {
b0db36b1
AL
466 Res = read(iFd,To,Size);
467 if (Res < 0 && errno == EINTR)
468 continue;
469 if (Res < 0)
470 {
471 Flags |= Fail;
b2e465d6 472 return _error->Errno("read",_("Read error"));
b0db36b1 473 }
578bfd0a 474
b0db36b1
AL
475 To = (char *)To + Res;
476 Size -= Res;
f604cf55
AL
477 if (Actual != 0)
478 *Actual += Res;
b0db36b1
AL
479 }
480 while (Res > 0 && Size > 0);
481
482 if (Size == 0)
483 return true;
484
ddc1d8d0 485 // Eof handling
f604cf55 486 if (Actual != 0)
ddc1d8d0
AL
487 {
488 Flags |= HitEof;
489 return true;
490 }
491
b0db36b1 492 Flags |= Fail;
b2e465d6 493 return _error->Error(_("read, still have %lu to read but none left"),Size);
578bfd0a
AL
494}
495 /*}}}*/
8e06abb2 496// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
497// ---------------------------------------------------------------------
498/* */
a05599f1 499bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 500{
b0db36b1
AL
501 int Res;
502 errno = 0;
503 do
578bfd0a 504 {
b0db36b1
AL
505 Res = write(iFd,From,Size);
506 if (Res < 0 && errno == EINTR)
507 continue;
508 if (Res < 0)
509 {
510 Flags |= Fail;
b2e465d6 511 return _error->Errno("write",_("Write error"));
b0db36b1
AL
512 }
513
514 From = (char *)From + Res;
515 Size -= Res;
578bfd0a 516 }
b0db36b1 517 while (Res > 0 && Size > 0);
578bfd0a 518
b0db36b1
AL
519 if (Size == 0)
520 return true;
521
522 Flags |= Fail;
b2e465d6 523 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
578bfd0a
AL
524}
525 /*}}}*/
8e06abb2 526// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
527// ---------------------------------------------------------------------
528/* */
8e06abb2 529bool FileFd::Seek(unsigned long To)
578bfd0a
AL
530{
531 if (lseek(iFd,To,SEEK_SET) != (signed)To)
532 {
533 Flags |= Fail;
b2e465d6 534 return _error->Error("Unable to seek to %lu",To);
578bfd0a
AL
535 }
536
727f18af
AL
537 return true;
538}
539 /*}}}*/
540// FileFd::Skip - Seek in the file /*{{{*/
541// ---------------------------------------------------------------------
542/* */
543bool FileFd::Skip(unsigned long Over)
544{
545 if (lseek(iFd,Over,SEEK_CUR) < 0)
546 {
547 Flags |= Fail;
b2e465d6 548 return _error->Error("Unable to seek ahead %lu",Over);
727f18af
AL
549 }
550
6d5dd02a
AL
551 return true;
552}
553 /*}}}*/
554// FileFd::Truncate - Truncate the file /*{{{*/
555// ---------------------------------------------------------------------
556/* */
557bool FileFd::Truncate(unsigned long To)
558{
559 if (ftruncate(iFd,To) != 0)
560 {
561 Flags |= Fail;
b2e465d6 562 return _error->Error("Unable to truncate to %lu",To);
6d5dd02a
AL
563 }
564
578bfd0a
AL
565 return true;
566}
567 /*}}}*/
7f25bdff
AL
568// FileFd::Tell - Current seek position /*{{{*/
569// ---------------------------------------------------------------------
570/* */
571unsigned long FileFd::Tell()
572{
573 off_t Res = lseek(iFd,0,SEEK_CUR);
574 if (Res == (off_t)-1)
575 _error->Errno("lseek","Failed to determine the current file position");
576 return Res;
577}
578 /*}}}*/
8e06abb2 579// FileFd::Size - Return the size of the file /*{{{*/
578bfd0a
AL
580// ---------------------------------------------------------------------
581/* */
8e06abb2 582unsigned long FileFd::Size()
578bfd0a
AL
583{
584 struct stat Buf;
585 if (fstat(iFd,&Buf) != 0)
586 return _error->Errno("fstat","Unable to determine the file size");
587 return Buf.st_size;
588}
589 /*}}}*/
8e06abb2 590// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
591// ---------------------------------------------------------------------
592/* */
8e06abb2 593bool FileFd::Close()
578bfd0a
AL
594{
595 bool Res = true;
596 if ((Flags & AutoClose) == AutoClose)
1164783d 597 if (iFd >= 0 && close(iFd) != 0)
b2e465d6 598 Res &= _error->Errno("close",_("Problem closing the file"));
1164783d
AL
599 iFd = -1;
600
578bfd0a
AL
601 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
602 FileName.empty() == false)
603 if (unlink(FileName.c_str()) != 0)
b2e465d6 604 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
578bfd0a
AL
605 return Res;
606}
607 /*}}}*/
b2e465d6
AL
608// FileFd::Sync - Sync the file /*{{{*/
609// ---------------------------------------------------------------------
610/* */
611bool FileFd::Sync()
612{
613#ifdef _POSIX_SYNCHRONIZED_IO
614 if (fsync(iFd) != 0)
615 return _error->Errno("sync",_("Problem syncing the file"));
616#endif
617 return true;
618}
619 /*}}}*/