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