merge with lp:~mvo/apt/debian-sid to get 0.7.25.1 and my changes back
[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 <dirent.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <set>
41 #include <algorithm>
42 /*}}}*/
43
44 using namespace std;
45
46 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
47 // ---------------------------------------------------------------------
48 /* */
49 bool RunScripts(const char *Cnf)
50 {
51 Configuration::Item const *Opts = _config->Tree(Cnf);
52 if (Opts == 0 || Opts->Child == 0)
53 return true;
54 Opts = Opts->Child;
55
56 // Fork for running the system calls
57 pid_t Child = ExecFork();
58
59 // This is the child
60 if (Child == 0)
61 {
62 if (chdir("/tmp/") != 0)
63 _exit(100);
64
65 unsigned int Count = 1;
66 for (; Opts != 0; Opts = Opts->Next, Count++)
67 {
68 if (Opts->Value.empty() == true)
69 continue;
70
71 if (system(Opts->Value.c_str()) != 0)
72 _exit(100+Count);
73 }
74 _exit(0);
75 }
76
77 // Wait for the child
78 int Status = 0;
79 while (waitpid(Child,&Status,0) != Child)
80 {
81 if (errno == EINTR)
82 continue;
83 return _error->Errno("waitpid","Couldn't wait for subprocess");
84 }
85
86 // Restore sig int/quit
87 signal(SIGQUIT,SIG_DFL);
88 signal(SIGINT,SIG_DFL);
89
90 // Check for an error code.
91 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
92 {
93 unsigned int Count = WEXITSTATUS(Status);
94 if (Count > 100)
95 {
96 Count -= 100;
97 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
98 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
99 }
100
101 return _error->Error("Sub-process returned an error code");
102 }
103
104 return true;
105 }
106 /*}}}*/
107
108 // CopyFile - Buffered copy of a file /*{{{*/
109 // ---------------------------------------------------------------------
110 /* The caller is expected to set things so that failure causes erasure */
111 bool CopyFile(FileFd &From,FileFd &To)
112 {
113 if (From.IsOpen() == false || To.IsOpen() == false)
114 return false;
115
116 // Buffered copy between fds
117 SPtrArray<unsigned char> Buf = new unsigned char[64000];
118 unsigned long Size = From.Size();
119 while (Size != 0)
120 {
121 unsigned long ToRead = Size;
122 if (Size > 64000)
123 ToRead = 64000;
124
125 if (From.Read(Buf,ToRead) == false ||
126 To.Write(Buf,ToRead) == false)
127 return false;
128
129 Size -= ToRead;
130 }
131
132 return true;
133 }
134 /*}}}*/
135 // GetLock - Gets a lock file /*{{{*/
136 // ---------------------------------------------------------------------
137 /* This will create an empty file of the given name and lock it. Once this
138 is done all other calls to GetLock in any other process will fail with
139 -1. The return result is the fd of the file, the call should call
140 close at some time. */
141 int GetLock(string File,bool Errors)
142 {
143 // GetLock() is used in aptitude on directories with public-write access
144 // Use O_NOFOLLOW here to prevent symlink traversal attacks
145 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
146 if (FD < 0)
147 {
148 // Read only .. cant have locking problems there.
149 if (errno == EROFS)
150 {
151 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
152 return dup(0); // Need something for the caller to close
153 }
154
155 if (Errors == true)
156 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
157
158 // Feh.. We do this to distinguish the lock vs open case..
159 errno = EPERM;
160 return -1;
161 }
162 SetCloseExec(FD,true);
163
164 // Aquire a write lock
165 struct flock fl;
166 fl.l_type = F_WRLCK;
167 fl.l_whence = SEEK_SET;
168 fl.l_start = 0;
169 fl.l_len = 0;
170 if (fcntl(FD,F_SETLK,&fl) == -1)
171 {
172 if (errno == ENOLCK)
173 {
174 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
175 return dup(0); // Need something for the caller to close
176 }
177 if (Errors == true)
178 _error->Errno("open",_("Could not get lock %s"),File.c_str());
179
180 int Tmp = errno;
181 close(FD);
182 errno = Tmp;
183 return -1;
184 }
185
186 return FD;
187 }
188 /*}}}*/
189 // FileExists - Check if a file exists /*{{{*/
190 // ---------------------------------------------------------------------
191 /* */
192 bool FileExists(string File)
193 {
194 struct stat Buf;
195 if (stat(File.c_str(),&Buf) != 0)
196 return false;
197 return true;
198 }
199 /*}}}*/
200 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
201 // ---------------------------------------------------------------------
202 /* If an extension is given only files with this extension are included
203 in the returned vector, otherwise every "normal" file is included. */
204 std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
205 bool const &SortList)
206 {
207 std::vector<string> List;
208 DIR *D = opendir(Dir.c_str());
209 if (D == 0)
210 {
211 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
212 return List;
213 }
214
215 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
216 {
217 if (Ent->d_name[0] == '.')
218 continue;
219
220 if (Ext.empty() == false && flExtension(Ent->d_name) != Ext)
221 continue;
222
223 // Skip bad file names ala run-parts
224 const char *C = Ent->d_name;
225 for (; *C != 0; ++C)
226 if (isalpha(*C) == 0 && isdigit(*C) == 0
227 && *C != '_' && *C != '-' && *C != '.')
228 break;
229
230 if (*C != 0)
231 continue;
232
233 // Make sure it is a file and not something else
234 string const File = flCombine(Dir,Ent->d_name);
235 struct stat St;
236 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
237 continue;
238
239 List.push_back(File);
240 }
241 closedir(D);
242
243 if (SortList == true)
244 std::sort(List.begin(),List.end());
245 return List;
246 }
247 /*}}}*/
248 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
249 // ---------------------------------------------------------------------
250 /* We return / on failure. */
251 string SafeGetCWD()
252 {
253 // Stash the current dir.
254 char S[300];
255 S[0] = 0;
256 if (getcwd(S,sizeof(S)-2) == 0)
257 return "/";
258 unsigned int Len = strlen(S);
259 S[Len] = '/';
260 S[Len+1] = 0;
261 return S;
262 }
263 /*}}}*/
264 // flNotDir - Strip the directory from the filename /*{{{*/
265 // ---------------------------------------------------------------------
266 /* */
267 string flNotDir(string File)
268 {
269 string::size_type Res = File.rfind('/');
270 if (Res == string::npos)
271 return File;
272 Res++;
273 return string(File,Res,Res - File.length());
274 }
275 /*}}}*/
276 // flNotFile - Strip the file from the directory name /*{{{*/
277 // ---------------------------------------------------------------------
278 /* Result ends in a / */
279 string flNotFile(string File)
280 {
281 string::size_type Res = File.rfind('/');
282 if (Res == string::npos)
283 return "./";
284 Res++;
285 return string(File,0,Res);
286 }
287 /*}}}*/
288 // flExtension - Return the extension for the file /*{{{*/
289 // ---------------------------------------------------------------------
290 /* */
291 string flExtension(string File)
292 {
293 string::size_type Res = File.rfind('.');
294 if (Res == string::npos)
295 return File;
296 Res++;
297 return string(File,Res,Res - File.length());
298 }
299 /*}}}*/
300 // flNoLink - If file is a symlink then deref it /*{{{*/
301 // ---------------------------------------------------------------------
302 /* If the name is not a link then the returned path is the input. */
303 string flNoLink(string File)
304 {
305 struct stat St;
306 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
307 return File;
308 if (stat(File.c_str(),&St) != 0)
309 return File;
310
311 /* Loop resolving the link. There is no need to limit the number of
312 loops because the stat call above ensures that the symlink is not
313 circular */
314 char Buffer[1024];
315 string NFile = File;
316 while (1)
317 {
318 // Read the link
319 int Res;
320 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
321 (unsigned)Res >= sizeof(Buffer))
322 return File;
323
324 // Append or replace the previous path
325 Buffer[Res] = 0;
326 if (Buffer[0] == '/')
327 NFile = Buffer;
328 else
329 NFile = flNotFile(NFile) + Buffer;
330
331 // See if we are done
332 if (lstat(NFile.c_str(),&St) != 0)
333 return File;
334 if (S_ISLNK(St.st_mode) == 0)
335 return NFile;
336 }
337 }
338 /*}}}*/
339 // flCombine - Combine a file and a directory /*{{{*/
340 // ---------------------------------------------------------------------
341 /* If the file is an absolute path then it is just returned, otherwise
342 the directory is pre-pended to it. */
343 string flCombine(string Dir,string File)
344 {
345 if (File.empty() == true)
346 return string();
347
348 if (File[0] == '/' || Dir.empty() == true)
349 return File;
350 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
351 return File;
352 if (Dir[Dir.length()-1] == '/')
353 return Dir + File;
354 return Dir + '/' + File;
355 }
356 /*}}}*/
357 // SetCloseExec - Set the close on exec flag /*{{{*/
358 // ---------------------------------------------------------------------
359 /* */
360 void SetCloseExec(int Fd,bool Close)
361 {
362 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
363 {
364 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
365 exit(100);
366 }
367 }
368 /*}}}*/
369 // SetNonBlock - Set the nonblocking flag /*{{{*/
370 // ---------------------------------------------------------------------
371 /* */
372 void SetNonBlock(int Fd,bool Block)
373 {
374 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
375 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
376 {
377 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
378 exit(100);
379 }
380 }
381 /*}}}*/
382 // WaitFd - Wait for a FD to become readable /*{{{*/
383 // ---------------------------------------------------------------------
384 /* This waits for a FD to become readable using select. It is useful for
385 applications making use of non-blocking sockets. The timeout is
386 in seconds. */
387 bool WaitFd(int Fd,bool write,unsigned long timeout)
388 {
389 fd_set Set;
390 struct timeval tv;
391 FD_ZERO(&Set);
392 FD_SET(Fd,&Set);
393 tv.tv_sec = timeout;
394 tv.tv_usec = 0;
395 if (write == true)
396 {
397 int Res;
398 do
399 {
400 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
401 }
402 while (Res < 0 && errno == EINTR);
403
404 if (Res <= 0)
405 return false;
406 }
407 else
408 {
409 int Res;
410 do
411 {
412 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
413 }
414 while (Res < 0 && errno == EINTR);
415
416 if (Res <= 0)
417 return false;
418 }
419
420 return true;
421 }
422 /*}}}*/
423 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
424 // ---------------------------------------------------------------------
425 /* This is used if you want to cleanse the environment for the forked
426 child, it fixes up the important signals and nukes all of the fds,
427 otherwise acts like normal fork. */
428 pid_t ExecFork()
429 {
430 // Fork off the process
431 pid_t Process = fork();
432 if (Process < 0)
433 {
434 cerr << "FATAL -> Failed to fork." << endl;
435 exit(100);
436 }
437
438 // Spawn the subprocess
439 if (Process == 0)
440 {
441 // Setup the signals
442 signal(SIGPIPE,SIG_DFL);
443 signal(SIGQUIT,SIG_DFL);
444 signal(SIGINT,SIG_DFL);
445 signal(SIGWINCH,SIG_DFL);
446 signal(SIGCONT,SIG_DFL);
447 signal(SIGTSTP,SIG_DFL);
448
449 set<int> KeepFDs;
450 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
451 if (Opts != 0 && Opts->Child != 0)
452 {
453 Opts = Opts->Child;
454 for (; Opts != 0; Opts = Opts->Next)
455 {
456 if (Opts->Value.empty() == true)
457 continue;
458 int fd = atoi(Opts->Value.c_str());
459 KeepFDs.insert(fd);
460 }
461 }
462
463 // Close all of our FDs - just in case
464 for (int K = 3; K != 40; K++)
465 {
466 if(KeepFDs.find(K) == KeepFDs.end())
467 fcntl(K,F_SETFD,FD_CLOEXEC);
468 }
469 }
470
471 return Process;
472 }
473 /*}}}*/
474 // ExecWait - Fancy waitpid /*{{{*/
475 // ---------------------------------------------------------------------
476 /* Waits for the given sub process. If Reap is set then no errors are
477 generated. Otherwise a failed subprocess will generate a proper descriptive
478 message */
479 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
480 {
481 if (Pid <= 1)
482 return true;
483
484 // Wait and collect the error code
485 int Status;
486 while (waitpid(Pid,&Status,0) != Pid)
487 {
488 if (errno == EINTR)
489 continue;
490
491 if (Reap == true)
492 return false;
493
494 return _error->Error(_("Waited for %s but it wasn't there"),Name);
495 }
496
497
498 // Check for an error code.
499 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
500 {
501 if (Reap == true)
502 return false;
503 if (WIFSIGNALED(Status) != 0)
504 {
505 if( WTERMSIG(Status) == SIGSEGV)
506 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
507 else
508 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
509 }
510
511 if (WIFEXITED(Status) != 0)
512 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
513
514 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
515 }
516
517 return true;
518 }
519 /*}}}*/
520
521 // FileFd::Open - Open a file /*{{{*/
522 // ---------------------------------------------------------------------
523 /* The most commonly used open mode combinations are given with Mode */
524 bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
525 {
526 Close();
527 Flags = AutoClose;
528 switch (Mode)
529 {
530 case ReadOnly:
531 iFd = open(FileName.c_str(),O_RDONLY);
532 break;
533
534 case WriteEmpty:
535 {
536 struct stat Buf;
537 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
538 unlink(FileName.c_str());
539 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
540 break;
541 }
542
543 case WriteExists:
544 iFd = open(FileName.c_str(),O_RDWR);
545 break;
546
547 case WriteAny:
548 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
549 break;
550
551 case WriteTemp:
552 unlink(FileName.c_str());
553 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
554 break;
555 }
556
557 if (iFd < 0)
558 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
559
560 this->FileName = FileName;
561 SetCloseExec(iFd,true);
562 return true;
563 }
564 /*}}}*/
565 // FileFd::~File - Closes the file /*{{{*/
566 // ---------------------------------------------------------------------
567 /* If the proper modes are selected then we close the Fd and possibly
568 unlink the file on error. */
569 FileFd::~FileFd()
570 {
571 Close();
572 }
573 /*}}}*/
574 // FileFd::Read - Read a bit of the file /*{{{*/
575 // ---------------------------------------------------------------------
576 /* We are carefull to handle interruption by a signal while reading
577 gracefully. */
578 bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
579 {
580 int Res;
581 errno = 0;
582 if (Actual != 0)
583 *Actual = 0;
584
585 do
586 {
587 Res = read(iFd,To,Size);
588 if (Res < 0 && errno == EINTR)
589 continue;
590 if (Res < 0)
591 {
592 Flags |= Fail;
593 return _error->Errno("read",_("Read error"));
594 }
595
596 To = (char *)To + Res;
597 Size -= Res;
598 if (Actual != 0)
599 *Actual += Res;
600 }
601 while (Res > 0 && Size > 0);
602
603 if (Size == 0)
604 return true;
605
606 // Eof handling
607 if (Actual != 0)
608 {
609 Flags |= HitEof;
610 return true;
611 }
612
613 Flags |= Fail;
614 return _error->Error(_("read, still have %lu to read but none left"),Size);
615 }
616 /*}}}*/
617 // FileFd::Write - Write to the file /*{{{*/
618 // ---------------------------------------------------------------------
619 /* */
620 bool FileFd::Write(const void *From,unsigned long Size)
621 {
622 int Res;
623 errno = 0;
624 do
625 {
626 Res = write(iFd,From,Size);
627 if (Res < 0 && errno == EINTR)
628 continue;
629 if (Res < 0)
630 {
631 Flags |= Fail;
632 return _error->Errno("write",_("Write error"));
633 }
634
635 From = (char *)From + Res;
636 Size -= Res;
637 }
638 while (Res > 0 && Size > 0);
639
640 if (Size == 0)
641 return true;
642
643 Flags |= Fail;
644 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
645 }
646 /*}}}*/
647 // FileFd::Seek - Seek in the file /*{{{*/
648 // ---------------------------------------------------------------------
649 /* */
650 bool FileFd::Seek(unsigned long To)
651 {
652 if (lseek(iFd,To,SEEK_SET) != (signed)To)
653 {
654 Flags |= Fail;
655 return _error->Error("Unable to seek to %lu",To);
656 }
657
658 return true;
659 }
660 /*}}}*/
661 // FileFd::Skip - Seek in the file /*{{{*/
662 // ---------------------------------------------------------------------
663 /* */
664 bool FileFd::Skip(unsigned long Over)
665 {
666 if (lseek(iFd,Over,SEEK_CUR) < 0)
667 {
668 Flags |= Fail;
669 return _error->Error("Unable to seek ahead %lu",Over);
670 }
671
672 return true;
673 }
674 /*}}}*/
675 // FileFd::Truncate - Truncate the file /*{{{*/
676 // ---------------------------------------------------------------------
677 /* */
678 bool FileFd::Truncate(unsigned long To)
679 {
680 if (ftruncate(iFd,To) != 0)
681 {
682 Flags |= Fail;
683 return _error->Error("Unable to truncate to %lu",To);
684 }
685
686 return true;
687 }
688 /*}}}*/
689 // FileFd::Tell - Current seek position /*{{{*/
690 // ---------------------------------------------------------------------
691 /* */
692 unsigned long FileFd::Tell()
693 {
694 off_t Res = lseek(iFd,0,SEEK_CUR);
695 if (Res == (off_t)-1)
696 _error->Errno("lseek","Failed to determine the current file position");
697 return Res;
698 }
699 /*}}}*/
700 // FileFd::Size - Return the size of the file /*{{{*/
701 // ---------------------------------------------------------------------
702 /* */
703 unsigned long FileFd::Size()
704 {
705 struct stat Buf;
706 if (fstat(iFd,&Buf) != 0)
707 return _error->Errno("fstat","Unable to determine the file size");
708 return Buf.st_size;
709 }
710 /*}}}*/
711 // FileFd::Close - Close the file if the close flag is set /*{{{*/
712 // ---------------------------------------------------------------------
713 /* */
714 bool FileFd::Close()
715 {
716 bool Res = true;
717 if ((Flags & AutoClose) == AutoClose)
718 if (iFd >= 0 && close(iFd) != 0)
719 Res &= _error->Errno("close",_("Problem closing the file"));
720 iFd = -1;
721
722 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
723 FileName.empty() == false)
724 if (unlink(FileName.c_str()) != 0)
725 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
726 return Res;
727 }
728 /*}}}*/
729 // FileFd::Sync - Sync the file /*{{{*/
730 // ---------------------------------------------------------------------
731 /* */
732 bool FileFd::Sync()
733 {
734 #ifdef _POSIX_SYNCHRONIZED_IO
735 if (fsync(iFd) != 0)
736 return _error->Errno("sync",_("Problem syncing the file"));
737 #endif
738 return true;
739 }
740 /*}}}*/