* Merged apt-authentication-reliabilty branch. This means
[ntk/apt.git] / apt-pkg / contrib / fileutl.cc
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 Most of this source is placed in the Public Domain, do with it what
12 you will
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
14
15 The exception is RunScripts() it is under the GPLv2
16
17 ##################################################################### */
18 /*}}}*/
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>
24
25 #include <apti18n.h>
26
27 #include <cstdlib>
28 #include <cstring>
29
30 #include <iostream>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/time.h>
36 #include <sys/wait.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <set>
40 /*}}}*/
41
42 using namespace std;
43
44 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
45 // ---------------------------------------------------------------------
46 /* */
47 bool RunScripts(const char *Cnf)
48 {
49 Configuration::Item const *Opts = _config->Tree(Cnf);
50 if (Opts == 0 || Opts->Child == 0)
51 return true;
52 Opts = Opts->Child;
53
54 // Fork for running the system calls
55 pid_t Child = ExecFork();
56
57 // This is the child
58 if (Child == 0)
59 {
60 if (chdir("/tmp/") != 0)
61 _exit(100);
62
63 unsigned int Count = 1;
64 for (; Opts != 0; Opts = Opts->Next, Count++)
65 {
66 if (Opts->Value.empty() == true)
67 continue;
68
69 if (system(Opts->Value.c_str()) != 0)
70 _exit(100+Count);
71 }
72 _exit(0);
73 }
74
75 // Wait for the child
76 int Status = 0;
77 while (waitpid(Child,&Status,0) != Child)
78 {
79 if (errno == EINTR)
80 continue;
81 return _error->Errno("waitpid","Couldn't wait for subprocess");
82 }
83
84 // Restore sig int/quit
85 signal(SIGQUIT,SIG_DFL);
86 signal(SIGINT,SIG_DFL);
87
88 // Check for an error code.
89 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
90 {
91 unsigned int Count = WEXITSTATUS(Status);
92 if (Count > 100)
93 {
94 Count -= 100;
95 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
96 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
97 }
98
99 return _error->Error("Sub-process returned an error code");
100 }
101
102 return true;
103 }
104 /*}}}*/
105
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)
110 {
111 if (From.IsOpen() == false || To.IsOpen() == false)
112 return false;
113
114 // Buffered copy between fds
115 SPtrArray<unsigned char> Buf = new unsigned char[64000];
116 unsigned long Size = From.Size();
117 while (Size != 0)
118 {
119 unsigned long ToRead = Size;
120 if (Size > 64000)
121 ToRead = 64000;
122
123 if (From.Read(Buf,ToRead) == false ||
124 To.Write(Buf,ToRead) == false)
125 return false;
126
127 Size -= ToRead;
128 }
129
130 return true;
131 }
132 /*}}}*/
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)
140 {
141 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_TRUNC,0640);
142 if (FD < 0)
143 {
144 // Read only .. cant have locking problems there.
145 if (errno == EROFS)
146 {
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
149 }
150
151 if (Errors == true)
152 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
153
154 // Feh.. We do this to distinguish the lock vs open case..
155 errno = EPERM;
156 return -1;
157 }
158 SetCloseExec(FD,true);
159
160 // Aquire a write lock
161 struct flock fl;
162 fl.l_type = F_WRLCK;
163 fl.l_whence = SEEK_SET;
164 fl.l_start = 0;
165 fl.l_len = 0;
166 if (fcntl(FD,F_SETLK,&fl) == -1)
167 {
168 if (errno == ENOLCK)
169 {
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
172 }
173 if (Errors == true)
174 _error->Errno("open",_("Could not get lock %s"),File.c_str());
175
176 int Tmp = errno;
177 close(FD);
178 errno = Tmp;
179 return -1;
180 }
181
182 return FD;
183 }
184 /*}}}*/
185 // FileExists - Check if a file exists /*{{{*/
186 // ---------------------------------------------------------------------
187 /* */
188 bool FileExists(string File)
189 {
190 struct stat Buf;
191 if (stat(File.c_str(),&Buf) != 0)
192 return false;
193 return true;
194 }
195 /*}}}*/
196 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
197 // ---------------------------------------------------------------------
198 /* We return / on failure. */
199 string SafeGetCWD()
200 {
201 // Stash the current dir.
202 char S[300];
203 S[0] = 0;
204 if (getcwd(S,sizeof(S)-2) == 0)
205 return "/";
206 unsigned int Len = strlen(S);
207 S[Len] = '/';
208 S[Len+1] = 0;
209 return S;
210 }
211 /*}}}*/
212 // flNotDir - Strip the directory from the filename /*{{{*/
213 // ---------------------------------------------------------------------
214 /* */
215 string flNotDir(string File)
216 {
217 string::size_type Res = File.rfind('/');
218 if (Res == string::npos)
219 return File;
220 Res++;
221 return string(File,Res,Res - File.length());
222 }
223 /*}}}*/
224 // flNotFile - Strip the file from the directory name /*{{{*/
225 // ---------------------------------------------------------------------
226 /* Result ends in a / */
227 string flNotFile(string File)
228 {
229 string::size_type Res = File.rfind('/');
230 if (Res == string::npos)
231 return "./";
232 Res++;
233 return string(File,0,Res);
234 }
235 /*}}}*/
236 // flExtension - Return the extension for the file /*{{{*/
237 // ---------------------------------------------------------------------
238 /* */
239 string flExtension(string File)
240 {
241 string::size_type Res = File.rfind('.');
242 if (Res == string::npos)
243 return File;
244 Res++;
245 return string(File,Res,Res - File.length());
246 }
247 /*}}}*/
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)
252 {
253 struct stat St;
254 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
255 return File;
256 if (stat(File.c_str(),&St) != 0)
257 return File;
258
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
261 circular */
262 char Buffer[1024];
263 string NFile = File;
264 while (1)
265 {
266 // Read the link
267 int Res;
268 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
269 (unsigned)Res >= sizeof(Buffer))
270 return File;
271
272 // Append or replace the previous path
273 Buffer[Res] = 0;
274 if (Buffer[0] == '/')
275 NFile = Buffer;
276 else
277 NFile = flNotFile(NFile) + Buffer;
278
279 // See if we are done
280 if (lstat(NFile.c_str(),&St) != 0)
281 return File;
282 if (S_ISLNK(St.st_mode) == 0)
283 return NFile;
284 }
285 }
286 /*}}}*/
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)
292 {
293 if (File.empty() == true)
294 return string();
295
296 if (File[0] == '/' || Dir.empty() == true)
297 return File;
298 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
299 return File;
300 if (Dir[Dir.length()-1] == '/')
301 return Dir + File;
302 return Dir + '/' + File;
303 }
304 /*}}}*/
305 // SetCloseExec - Set the close on exec flag /*{{{*/
306 // ---------------------------------------------------------------------
307 /* */
308 void SetCloseExec(int Fd,bool Close)
309 {
310 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
311 {
312 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
313 exit(100);
314 }
315 }
316 /*}}}*/
317 // SetNonBlock - Set the nonblocking flag /*{{{*/
318 // ---------------------------------------------------------------------
319 /* */
320 void SetNonBlock(int Fd,bool Block)
321 {
322 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
323 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
324 {
325 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
326 exit(100);
327 }
328 }
329 /*}}}*/
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
334 in seconds. */
335 bool WaitFd(int Fd,bool write,unsigned long timeout)
336 {
337 fd_set Set;
338 struct timeval tv;
339 FD_ZERO(&Set);
340 FD_SET(Fd,&Set);
341 tv.tv_sec = timeout;
342 tv.tv_usec = 0;
343 if (write == true)
344 {
345 int Res;
346 do
347 {
348 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
349 }
350 while (Res < 0 && errno == EINTR);
351
352 if (Res <= 0)
353 return false;
354 }
355 else
356 {
357 int Res;
358 do
359 {
360 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
361 }
362 while (Res < 0 && errno == EINTR);
363
364 if (Res <= 0)
365 return false;
366 }
367
368 return true;
369 }
370 /*}}}*/
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. */
376 pid_t ExecFork()
377 {
378 // Fork off the process
379 pid_t Process = fork();
380 if (Process < 0)
381 {
382 cerr << "FATAL -> Failed to fork." << endl;
383 exit(100);
384 }
385
386 // Spawn the subprocess
387 if (Process == 0)
388 {
389 // Setup the signals
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);
396
397 set<int> KeepFDs;
398 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
399 if (Opts != 0 && Opts->Child != 0)
400 {
401 Opts = Opts->Child;
402 for (; Opts != 0; Opts = Opts->Next)
403 {
404 if (Opts->Value.empty() == true)
405 continue;
406 int fd = atoi(Opts->Value.c_str());
407 KeepFDs.insert(fd);
408 }
409 }
410
411 // Close all of our FDs - just in case
412 for (int K = 3; K != 40; K++)
413 {
414 if(KeepFDs.find(K) == KeepFDs.end())
415 fcntl(K,F_SETFD,FD_CLOEXEC);
416 }
417 }
418
419 return Process;
420 }
421 /*}}}*/
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
426 message */
427 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
428 {
429 if (Pid <= 1)
430 return true;
431
432 // Wait and collect the error code
433 int Status;
434 while (waitpid(Pid,&Status,0) != Pid)
435 {
436 if (errno == EINTR)
437 continue;
438
439 if (Reap == true)
440 return false;
441
442 return _error->Error(_("Waited for %s but it wasn't there"),Name);
443 }
444
445
446 // Check for an error code.
447 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
448 {
449 if (Reap == true)
450 return false;
451 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
452 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
453
454 if (WIFEXITED(Status) != 0)
455 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
456
457 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
458 }
459
460 return true;
461 }
462 /*}}}*/
463
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)
468 {
469 Close();
470 Flags = AutoClose;
471 switch (Mode)
472 {
473 case ReadOnly:
474 iFd = open(FileName.c_str(),O_RDONLY);
475 break;
476
477 case WriteEmpty:
478 {
479 struct stat Buf;
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);
483 break;
484 }
485
486 case WriteExists:
487 iFd = open(FileName.c_str(),O_RDWR);
488 break;
489
490 case WriteAny:
491 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
492 break;
493
494 case WriteTemp:
495 unlink(FileName.c_str());
496 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
497 break;
498 }
499
500 if (iFd < 0)
501 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
502
503 this->FileName = FileName;
504 SetCloseExec(iFd,true);
505 return true;
506 }
507 /*}}}*/
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. */
512 FileFd::~FileFd()
513 {
514 Close();
515 }
516 /*}}}*/
517 // FileFd::Read - Read a bit of the file /*{{{*/
518 // ---------------------------------------------------------------------
519 /* We are carefull to handle interruption by a signal while reading
520 gracefully. */
521 bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
522 {
523 int Res;
524 errno = 0;
525 if (Actual != 0)
526 *Actual = 0;
527
528 do
529 {
530 Res = read(iFd,To,Size);
531 if (Res < 0 && errno == EINTR)
532 continue;
533 if (Res < 0)
534 {
535 Flags |= Fail;
536 return _error->Errno("read",_("Read error"));
537 }
538
539 To = (char *)To + Res;
540 Size -= Res;
541 if (Actual != 0)
542 *Actual += Res;
543 }
544 while (Res > 0 && Size > 0);
545
546 if (Size == 0)
547 return true;
548
549 // Eof handling
550 if (Actual != 0)
551 {
552 Flags |= HitEof;
553 return true;
554 }
555
556 Flags |= Fail;
557 return _error->Error(_("read, still have %lu to read but none left"),Size);
558 }
559 /*}}}*/
560 // FileFd::Write - Write to the file /*{{{*/
561 // ---------------------------------------------------------------------
562 /* */
563 bool FileFd::Write(const void *From,unsigned long Size)
564 {
565 int Res;
566 errno = 0;
567 do
568 {
569 Res = write(iFd,From,Size);
570 if (Res < 0 && errno == EINTR)
571 continue;
572 if (Res < 0)
573 {
574 Flags |= Fail;
575 return _error->Errno("write",_("Write error"));
576 }
577
578 From = (char *)From + Res;
579 Size -= Res;
580 }
581 while (Res > 0 && Size > 0);
582
583 if (Size == 0)
584 return true;
585
586 Flags |= Fail;
587 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
588 }
589 /*}}}*/
590 // FileFd::Seek - Seek in the file /*{{{*/
591 // ---------------------------------------------------------------------
592 /* */
593 bool FileFd::Seek(unsigned long To)
594 {
595 if (lseek(iFd,To,SEEK_SET) != (signed)To)
596 {
597 Flags |= Fail;
598 return _error->Error("Unable to seek to %lu",To);
599 }
600
601 return true;
602 }
603 /*}}}*/
604 // FileFd::Skip - Seek in the file /*{{{*/
605 // ---------------------------------------------------------------------
606 /* */
607 bool FileFd::Skip(unsigned long Over)
608 {
609 if (lseek(iFd,Over,SEEK_CUR) < 0)
610 {
611 Flags |= Fail;
612 return _error->Error("Unable to seek ahead %lu",Over);
613 }
614
615 return true;
616 }
617 /*}}}*/
618 // FileFd::Truncate - Truncate the file /*{{{*/
619 // ---------------------------------------------------------------------
620 /* */
621 bool FileFd::Truncate(unsigned long To)
622 {
623 if (ftruncate(iFd,To) != 0)
624 {
625 Flags |= Fail;
626 return _error->Error("Unable to truncate to %lu",To);
627 }
628
629 return true;
630 }
631 /*}}}*/
632 // FileFd::Tell - Current seek position /*{{{*/
633 // ---------------------------------------------------------------------
634 /* */
635 unsigned long FileFd::Tell()
636 {
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");
640 return Res;
641 }
642 /*}}}*/
643 // FileFd::Size - Return the size of the file /*{{{*/
644 // ---------------------------------------------------------------------
645 /* */
646 unsigned long FileFd::Size()
647 {
648 struct stat Buf;
649 if (fstat(iFd,&Buf) != 0)
650 return _error->Errno("fstat","Unable to determine the file size");
651 return Buf.st_size;
652 }
653 /*}}}*/
654 // FileFd::Close - Close the file if the close flag is set /*{{{*/
655 // ---------------------------------------------------------------------
656 /* */
657 bool FileFd::Close()
658 {
659 bool Res = true;
660 if ((Flags & AutoClose) == AutoClose)
661 if (iFd >= 0 && close(iFd) != 0)
662 Res &= _error->Errno("close",_("Problem closing the file"));
663 iFd = -1;
664
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"));
669 return Res;
670 }
671 /*}}}*/
672 // FileFd::Sync - Sync the file /*{{{*/
673 // ---------------------------------------------------------------------
674 /* */
675 bool FileFd::Sync()
676 {
677 #ifdef _POSIX_SYNCHRONIZED_IO
678 if (fsync(iFd) != 0)
679 return _error->Errno("sync",_("Problem syncing the file"));
680 #endif
681 return true;
682 }
683 /*}}}*/