]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/fileutl.cc
* add "purge" commandline argument, closes: #133421)
[apt.git] / apt-pkg / contrib / fileutl.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
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
12 It was originally written by Jason Gunthorpe <jgg@debian.org>.
13
14 ##################################################################### */
15 /*}}}*/
16// Include Files /*{{{*/
17#ifdef __GNUG__
18#pragma implementation "apt-pkg/fileutl.h"
19#endif
20#include <apt-pkg/fileutl.h>
21#include <apt-pkg/error.h>
22#include <apt-pkg/sptr.h>
23#include <apt-pkg/configuration.h>
24
25#include <apti18n.h>
26
27#include <cstdlib>
28#include <iostream>
29#include <unistd.h>
30#include <fcntl.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/time.h>
34#include <sys/wait.h>
35#include <signal.h>
36#include <errno.h>
37#include <set>
38 /*}}}*/
39
40using namespace std;
41
42// CopyFile - Buffered copy of a file /*{{{*/
43// ---------------------------------------------------------------------
44/* The caller is expected to set things so that failure causes erasure */
45bool CopyFile(FileFd &From,FileFd &To)
46{
47 if (From.IsOpen() == false || To.IsOpen() == false)
48 return false;
49
50 // Buffered copy between fds
51 SPtrArray<unsigned char> Buf = new unsigned char[64000];
52 unsigned long Size = From.Size();
53 while (Size != 0)
54 {
55 unsigned long ToRead = Size;
56 if (Size > 64000)
57 ToRead = 64000;
58
59 if (From.Read(Buf,ToRead) == false ||
60 To.Write(Buf,ToRead) == false)
61 return false;
62
63 Size -= ToRead;
64 }
65
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 {
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
87 if (Errors == true)
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;
92 return -1;
93 }
94 SetCloseExec(FD,true);
95
96 // Aquire a write lock
97 struct flock fl;
98 fl.l_type = F_WRLCK;
99 fl.l_whence = SEEK_SET;
100 fl.l_start = 0;
101 fl.l_len = 0;
102 if (fcntl(FD,F_SETLK,&fl) == -1)
103 {
104 if (errno == ENOLCK)
105 {
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
108 }
109 if (Errors == true)
110 _error->Errno("open",_("Could not get lock %s"),File.c_str());
111
112 int Tmp = errno;
113 close(FD);
114 errno = Tmp;
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;
140 if (getcwd(S,sizeof(S)-2) == 0)
141 return "/";
142 unsigned int Len = strlen(S);
143 S[Len] = '/';
144 S[Len+1] = 0;
145 return S;
146}
147 /*}}}*/
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 /*}}}*/
160// flNotFile - Strip the file from the directory name /*{{{*/
161// ---------------------------------------------------------------------
162/* Result ends in a / */
163string flNotFile(string File)
164{
165 string::size_type Res = File.rfind('/');
166 if (Res == string::npos)
167 return "./";
168 Res++;
169 return string(File,0,Res);
170}
171 /*}}}*/
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 /*}}}*/
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 /*}}}*/
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 /*}}}*/
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{
258 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
259 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
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// ---------------------------------------------------------------------
268/* This waits for a FD to become readable using select. It is useful for
269 applications making use of non-blocking sockets. The timeout is
270 in seconds. */
271bool WaitFd(int Fd,bool write,unsigned long timeout)
272{
273 fd_set Set;
274 struct timeval tv;
275 FD_ZERO(&Set);
276 FD_SET(Fd,&Set);
277 tv.tv_sec = timeout;
278 tv.tv_usec = 0;
279 if (write == true)
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;
290 }
291 else
292 {
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;
302 }
303
304 return true;
305}
306 /*}}}*/
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. */
312pid_t ExecFork()
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);
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
347 // Close all of our FDs - just in case
348 for (int K = 3; K != 40; K++)
349 {
350 if(KeepFDs.find(K) == KeepFDs.end())
351 fcntl(K,F_SETFD,FD_CLOEXEC);
352 }
353 }
354
355 return Process;
356}
357 /*}}}*/
358// ExecWait - Fancy waitpid /*{{{*/
359// ---------------------------------------------------------------------
360/* Waits for the given sub process. If Reap is set then no errors are
361 generated. Otherwise a failed subprocess will generate a proper descriptive
362 message */
363bool ExecWait(pid_t Pid,const char *Name,bool Reap)
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
378 return _error->Error(_("Waited for %s but it wasn't there"),Name);
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)
388 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
389
390 if (WIFEXITED(Status) != 0)
391 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
392
393 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
394 }
395
396 return true;
397}
398 /*}}}*/
399
400// FileFd::Open - Open a file /*{{{*/
401// ---------------------------------------------------------------------
402/* The most commonly used open mode combinations are given with Mode */
403bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
404{
405 Close();
406 Flags = AutoClose;
407 switch (Mode)
408 {
409 case ReadOnly:
410 iFd = open(FileName.c_str(),O_RDONLY);
411 break;
412
413 case WriteEmpty:
414 {
415 struct stat Buf;
416 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
417 unlink(FileName.c_str());
418 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
419 break;
420 }
421
422 case WriteExists:
423 iFd = open(FileName.c_str(),O_RDWR);
424 break;
425
426 case WriteAny:
427 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
428 break;
429
430 case WriteTemp:
431 unlink(FileName.c_str());
432 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
433 break;
434 }
435
436 if (iFd < 0)
437 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
438
439 this->FileName = FileName;
440 SetCloseExec(iFd,true);
441 return true;
442}
443 /*}}}*/
444// FileFd::~File - Closes the file /*{{{*/
445// ---------------------------------------------------------------------
446/* If the proper modes are selected then we close the Fd and possibly
447 unlink the file on error. */
448FileFd::~FileFd()
449{
450 Close();
451}
452 /*}}}*/
453// FileFd::Read - Read a bit of the file /*{{{*/
454// ---------------------------------------------------------------------
455/* We are carefull to handle interruption by a signal while reading
456 gracefully. */
457bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
458{
459 int Res;
460 errno = 0;
461 if (Actual != 0)
462 *Actual = 0;
463
464 do
465 {
466 Res = read(iFd,To,Size);
467 if (Res < 0 && errno == EINTR)
468 continue;
469 if (Res < 0)
470 {
471 Flags |= Fail;
472 return _error->Errno("read",_("Read error"));
473 }
474
475 To = (char *)To + Res;
476 Size -= Res;
477 if (Actual != 0)
478 *Actual += Res;
479 }
480 while (Res > 0 && Size > 0);
481
482 if (Size == 0)
483 return true;
484
485 // Eof handling
486 if (Actual != 0)
487 {
488 Flags |= HitEof;
489 return true;
490 }
491
492 Flags |= Fail;
493 return _error->Error(_("read, still have %lu to read but none left"),Size);
494}
495 /*}}}*/
496// FileFd::Write - Write to the file /*{{{*/
497// ---------------------------------------------------------------------
498/* */
499bool FileFd::Write(const void *From,unsigned long Size)
500{
501 int Res;
502 errno = 0;
503 do
504 {
505 Res = write(iFd,From,Size);
506 if (Res < 0 && errno == EINTR)
507 continue;
508 if (Res < 0)
509 {
510 Flags |= Fail;
511 return _error->Errno("write",_("Write error"));
512 }
513
514 From = (char *)From + Res;
515 Size -= Res;
516 }
517 while (Res > 0 && Size > 0);
518
519 if (Size == 0)
520 return true;
521
522 Flags |= Fail;
523 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
524}
525 /*}}}*/
526// FileFd::Seek - Seek in the file /*{{{*/
527// ---------------------------------------------------------------------
528/* */
529bool FileFd::Seek(unsigned long To)
530{
531 if (lseek(iFd,To,SEEK_SET) != (signed)To)
532 {
533 Flags |= Fail;
534 return _error->Error("Unable to seek to %lu",To);
535 }
536
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;
548 return _error->Error("Unable to seek ahead %lu",Over);
549 }
550
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;
562 return _error->Error("Unable to truncate to %lu",To);
563 }
564
565 return true;
566}
567 /*}}}*/
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 /*}}}*/
579// FileFd::Size - Return the size of the file /*{{{*/
580// ---------------------------------------------------------------------
581/* */
582unsigned long FileFd::Size()
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 /*}}}*/
590// FileFd::Close - Close the file if the close flag is set /*{{{*/
591// ---------------------------------------------------------------------
592/* */
593bool FileFd::Close()
594{
595 bool Res = true;
596 if ((Flags & AutoClose) == AutoClose)
597 if (iFd >= 0 && close(iFd) != 0)
598 Res &= _error->Errno("close",_("Problem closing the file"));
599 iFd = -1;
600
601 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
602 FileName.empty() == false)
603 if (unlink(FileName.c_str()) != 0)
604 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
605 return Res;
606}
607 /*}}}*/
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 /*}}}*/