]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
eabaadf908ee7f29c637d87cb88b855d96c056df
[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 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
15
16 The exception is RunScripts() it is under the GPLv2
17
18 ##################################################################### */
19 /*}}}*/
20 // Include Files /*{{{*/
21 #include <apt-pkg/fileutl.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/sptr.h>
25 #include <apt-pkg/configuration.h>
26
27 #include <apti18n.h>
28
29 #include <cstdlib>
30 #include <cstring>
31 #include <cstdio>
32
33 #include <iostream>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <sys/wait.h>
40 #include <dirent.h>
41 #include <signal.h>
42 #include <errno.h>
43 #include <set>
44 #include <algorithm>
45 /*}}}*/
46
47 using namespace std;
48
49 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
50 // ---------------------------------------------------------------------
51 /* */
52 bool RunScripts(const char *Cnf)
53 {
54 Configuration::Item const *Opts = _config->Tree(Cnf);
55 if (Opts == 0 || Opts->Child == 0)
56 return true;
57 Opts = Opts->Child;
58
59 // Fork for running the system calls
60 pid_t Child = ExecFork();
61
62 // This is the child
63 if (Child == 0)
64 {
65 if (chdir("/tmp/") != 0)
66 _exit(100);
67
68 unsigned int Count = 1;
69 for (; Opts != 0; Opts = Opts->Next, Count++)
70 {
71 if (Opts->Value.empty() == true)
72 continue;
73
74 if (system(Opts->Value.c_str()) != 0)
75 _exit(100+Count);
76 }
77 _exit(0);
78 }
79
80 // Wait for the child
81 int Status = 0;
82 while (waitpid(Child,&Status,0) != Child)
83 {
84 if (errno == EINTR)
85 continue;
86 return _error->Errno("waitpid","Couldn't wait for subprocess");
87 }
88
89 // Restore sig int/quit
90 signal(SIGQUIT,SIG_DFL);
91 signal(SIGINT,SIG_DFL);
92
93 // Check for an error code.
94 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
95 {
96 unsigned int Count = WEXITSTATUS(Status);
97 if (Count > 100)
98 {
99 Count -= 100;
100 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
101 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
102 }
103
104 return _error->Error("Sub-process returned an error code");
105 }
106
107 return true;
108 }
109 /*}}}*/
110
111 // CopyFile - Buffered copy of a file /*{{{*/
112 // ---------------------------------------------------------------------
113 /* The caller is expected to set things so that failure causes erasure */
114 bool CopyFile(FileFd &From,FileFd &To)
115 {
116 if (From.IsOpen() == false || To.IsOpen() == false)
117 return false;
118
119 // Buffered copy between fds
120 SPtrArray<unsigned char> Buf = new unsigned char[64000];
121 unsigned long Size = From.Size();
122 while (Size != 0)
123 {
124 unsigned long ToRead = Size;
125 if (Size > 64000)
126 ToRead = 64000;
127
128 if (From.Read(Buf,ToRead) == false ||
129 To.Write(Buf,ToRead) == false)
130 return false;
131
132 Size -= ToRead;
133 }
134
135 return true;
136 }
137 /*}}}*/
138 // GetLock - Gets a lock file /*{{{*/
139 // ---------------------------------------------------------------------
140 /* This will create an empty file of the given name and lock it. Once this
141 is done all other calls to GetLock in any other process will fail with
142 -1. The return result is the fd of the file, the call should call
143 close at some time. */
144 int GetLock(string File,bool Errors)
145 {
146 // GetLock() is used in aptitude on directories with public-write access
147 // Use O_NOFOLLOW here to prevent symlink traversal attacks
148 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
149 if (FD < 0)
150 {
151 // Read only .. cant have locking problems there.
152 if (errno == EROFS)
153 {
154 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
155 return dup(0); // Need something for the caller to close
156 }
157
158 if (Errors == true)
159 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
160
161 // Feh.. We do this to distinguish the lock vs open case..
162 errno = EPERM;
163 return -1;
164 }
165 SetCloseExec(FD,true);
166
167 // Aquire a write lock
168 struct flock fl;
169 fl.l_type = F_WRLCK;
170 fl.l_whence = SEEK_SET;
171 fl.l_start = 0;
172 fl.l_len = 0;
173 if (fcntl(FD,F_SETLK,&fl) == -1)
174 {
175 if (errno == ENOLCK)
176 {
177 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
178 return dup(0); // Need something for the caller to close
179 }
180 if (Errors == true)
181 _error->Errno("open",_("Could not get lock %s"),File.c_str());
182
183 int Tmp = errno;
184 close(FD);
185 errno = Tmp;
186 return -1;
187 }
188
189 return FD;
190 }
191 /*}}}*/
192 // FileExists - Check if a file exists /*{{{*/
193 // ---------------------------------------------------------------------
194 /* */
195 bool FileExists(string File)
196 {
197 struct stat Buf;
198 if (stat(File.c_str(),&Buf) != 0)
199 return false;
200 return true;
201 }
202 /*}}}*/
203 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
204 // ---------------------------------------------------------------------
205 /* */
206 bool DirectoryExists(string const &Path)
207 {
208 struct stat Buf;
209 if (stat(Path.c_str(),&Buf) != 0)
210 return false;
211 return ((Buf.st_mode & S_IFDIR) != 0);
212 }
213 /*}}}*/
214 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
215 // ---------------------------------------------------------------------
216 /* This method will create all directories needed for path in good old
217 mkdir -p style but refuses to do this if Parent is not a prefix of
218 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
219 so it will create apt/archives if /var/cache exists - on the other
220 hand if the parent is /var/lib the creation will fail as this path
221 is not a parent of the path to be generated. */
222 bool CreateDirectory(string const &Parent, string const &Path)
223 {
224 if (Parent.empty() == true || Path.empty() == true)
225 return false;
226
227 if (DirectoryExists(Path) == true)
228 return true;
229
230 if (DirectoryExists(Parent) == false)
231 return false;
232
233 // we are not going to create directories "into the blue"
234 if (Path.find(Parent, 0) != 0)
235 return false;
236
237 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
238 string progress = Parent;
239 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
240 {
241 if (d->empty() == true)
242 continue;
243
244 progress.append("/").append(*d);
245 if (DirectoryExists(progress) == true)
246 continue;
247
248 if (mkdir(progress.c_str(), 0755) != 0)
249 return false;
250 }
251 return true;
252 }
253 /*}}}*/
254 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
255 // ---------------------------------------------------------------------
256 /* a small wrapper around CreateDirectory to check if it exists and to
257 remove the trailing "/apt/" from the parent directory if needed */
258 bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
259 {
260 if (DirectoryExists(Path) == true)
261 return true;
262
263 size_t const len = Parent.size();
264 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
265 {
266 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
267 return true;
268 }
269 else if (CreateDirectory(Parent, Path) == true)
270 return true;
271
272 return false;
273 }
274 /*}}}*/
275 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
276 // ---------------------------------------------------------------------
277 /* If an extension is given only files with this extension are included
278 in the returned vector, otherwise every "normal" file is included. */
279 std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
280 bool const &SortList, bool const &AllowNoExt)
281 {
282 std::vector<string> ext;
283 ext.reserve(2);
284 if (Ext.empty() == false)
285 ext.push_back(Ext);
286 if (AllowNoExt == true && ext.empty() == false)
287 ext.push_back("");
288 return GetListOfFilesInDir(Dir, ext, SortList);
289 }
290 std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
291 bool const &SortList)
292 {
293 // Attention debuggers: need to be set with the environment config file!
294 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
295 if (Debug == true)
296 {
297 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
298 if (Ext.empty() == true)
299 std::clog << "\tNO extension" << std::endl;
300 else
301 for (std::vector<string>::const_iterator e = Ext.begin();
302 e != Ext.end(); ++e)
303 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
304 }
305
306 std::vector<string> List;
307 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
308 DIR *D = opendir(Dir.c_str());
309 if (D == 0)
310 {
311 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
312 return List;
313 }
314
315 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
316 {
317 // skip "hidden" files
318 if (Ent->d_name[0] == '.')
319 continue;
320
321 // check for accepted extension:
322 // no extension given -> periods are bad as hell!
323 // extensions given -> "" extension allows no extension
324 if (Ext.empty() == false)
325 {
326 string d_ext = flExtension(Ent->d_name);
327 if (d_ext == Ent->d_name) // no extension
328 {
329 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
330 {
331 if (Debug == true)
332 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
333 if (SilentIgnore.Match(Ent->d_name) == false)
334 _error->Notice("Ignoring file '%s' in directory '%s' as it has no filename extension", Ent->d_name, Dir.c_str());
335 continue;
336 }
337 }
338 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
339 {
340 if (Debug == true)
341 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
342 if (SilentIgnore.Match(Ent->d_name) == false)
343 _error->Notice("Ignoring file '%s' in directory '%s' as it has an invalid filename extension", Ent->d_name, Dir.c_str());
344 continue;
345 }
346 }
347
348 // Skip bad filenames ala run-parts
349 const char *C = Ent->d_name;
350 for (; *C != 0; ++C)
351 if (isalpha(*C) == 0 && isdigit(*C) == 0
352 && *C != '_' && *C != '-') {
353 // no required extension -> dot is a bad character
354 if (*C == '.' && Ext.empty() == false)
355 continue;
356 break;
357 }
358
359 // we don't reach the end of the name -> bad character included
360 if (*C != 0)
361 {
362 if (Debug == true)
363 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
364 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
365 continue;
366 }
367
368 // skip filenames which end with a period. These are never valid
369 if (*(C - 1) == '.')
370 {
371 if (Debug == true)
372 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
373 continue;
374 }
375
376 // Make sure it is a file and not something else
377 string const File = flCombine(Dir,Ent->d_name);
378 struct stat St;
379 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
380 {
381 if (Debug == true)
382 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
383 continue;
384 }
385
386 if (Debug == true)
387 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
388 List.push_back(File);
389 }
390 closedir(D);
391
392 if (SortList == true)
393 std::sort(List.begin(),List.end());
394 return List;
395 }
396 /*}}}*/
397 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
398 // ---------------------------------------------------------------------
399 /* We return / on failure. */
400 string SafeGetCWD()
401 {
402 // Stash the current dir.
403 char S[300];
404 S[0] = 0;
405 if (getcwd(S,sizeof(S)-2) == 0)
406 return "/";
407 unsigned int Len = strlen(S);
408 S[Len] = '/';
409 S[Len+1] = 0;
410 return S;
411 }
412 /*}}}*/
413 // flNotDir - Strip the directory from the filename /*{{{*/
414 // ---------------------------------------------------------------------
415 /* */
416 string flNotDir(string File)
417 {
418 string::size_type Res = File.rfind('/');
419 if (Res == string::npos)
420 return File;
421 Res++;
422 return string(File,Res,Res - File.length());
423 }
424 /*}}}*/
425 // flNotFile - Strip the file from the directory name /*{{{*/
426 // ---------------------------------------------------------------------
427 /* Result ends in a / */
428 string flNotFile(string File)
429 {
430 string::size_type Res = File.rfind('/');
431 if (Res == string::npos)
432 return "./";
433 Res++;
434 return string(File,0,Res);
435 }
436 /*}}}*/
437 // flExtension - Return the extension for the file /*{{{*/
438 // ---------------------------------------------------------------------
439 /* */
440 string flExtension(string File)
441 {
442 string::size_type Res = File.rfind('.');
443 if (Res == string::npos)
444 return File;
445 Res++;
446 return string(File,Res,Res - File.length());
447 }
448 /*}}}*/
449 // flNoLink - If file is a symlink then deref it /*{{{*/
450 // ---------------------------------------------------------------------
451 /* If the name is not a link then the returned path is the input. */
452 string flNoLink(string File)
453 {
454 struct stat St;
455 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
456 return File;
457 if (stat(File.c_str(),&St) != 0)
458 return File;
459
460 /* Loop resolving the link. There is no need to limit the number of
461 loops because the stat call above ensures that the symlink is not
462 circular */
463 char Buffer[1024];
464 string NFile = File;
465 while (1)
466 {
467 // Read the link
468 int Res;
469 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
470 (unsigned)Res >= sizeof(Buffer))
471 return File;
472
473 // Append or replace the previous path
474 Buffer[Res] = 0;
475 if (Buffer[0] == '/')
476 NFile = Buffer;
477 else
478 NFile = flNotFile(NFile) + Buffer;
479
480 // See if we are done
481 if (lstat(NFile.c_str(),&St) != 0)
482 return File;
483 if (S_ISLNK(St.st_mode) == 0)
484 return NFile;
485 }
486 }
487 /*}}}*/
488 // flCombine - Combine a file and a directory /*{{{*/
489 // ---------------------------------------------------------------------
490 /* If the file is an absolute path then it is just returned, otherwise
491 the directory is pre-pended to it. */
492 string flCombine(string Dir,string File)
493 {
494 if (File.empty() == true)
495 return string();
496
497 if (File[0] == '/' || Dir.empty() == true)
498 return File;
499 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
500 return File;
501 if (Dir[Dir.length()-1] == '/')
502 return Dir + File;
503 return Dir + '/' + File;
504 }
505 /*}}}*/
506 // SetCloseExec - Set the close on exec flag /*{{{*/
507 // ---------------------------------------------------------------------
508 /* */
509 void SetCloseExec(int Fd,bool Close)
510 {
511 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
512 {
513 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
514 exit(100);
515 }
516 }
517 /*}}}*/
518 // SetNonBlock - Set the nonblocking flag /*{{{*/
519 // ---------------------------------------------------------------------
520 /* */
521 void SetNonBlock(int Fd,bool Block)
522 {
523 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
524 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
525 {
526 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
527 exit(100);
528 }
529 }
530 /*}}}*/
531 // WaitFd - Wait for a FD to become readable /*{{{*/
532 // ---------------------------------------------------------------------
533 /* This waits for a FD to become readable using select. It is useful for
534 applications making use of non-blocking sockets. The timeout is
535 in seconds. */
536 bool WaitFd(int Fd,bool write,unsigned long timeout)
537 {
538 fd_set Set;
539 struct timeval tv;
540 FD_ZERO(&Set);
541 FD_SET(Fd,&Set);
542 tv.tv_sec = timeout;
543 tv.tv_usec = 0;
544 if (write == true)
545 {
546 int Res;
547 do
548 {
549 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
550 }
551 while (Res < 0 && errno == EINTR);
552
553 if (Res <= 0)
554 return false;
555 }
556 else
557 {
558 int Res;
559 do
560 {
561 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
562 }
563 while (Res < 0 && errno == EINTR);
564
565 if (Res <= 0)
566 return false;
567 }
568
569 return true;
570 }
571 /*}}}*/
572 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
573 // ---------------------------------------------------------------------
574 /* This is used if you want to cleanse the environment for the forked
575 child, it fixes up the important signals and nukes all of the fds,
576 otherwise acts like normal fork. */
577 pid_t ExecFork()
578 {
579 // Fork off the process
580 pid_t Process = fork();
581 if (Process < 0)
582 {
583 cerr << "FATAL -> Failed to fork." << endl;
584 exit(100);
585 }
586
587 // Spawn the subprocess
588 if (Process == 0)
589 {
590 // Setup the signals
591 signal(SIGPIPE,SIG_DFL);
592 signal(SIGQUIT,SIG_DFL);
593 signal(SIGINT,SIG_DFL);
594 signal(SIGWINCH,SIG_DFL);
595 signal(SIGCONT,SIG_DFL);
596 signal(SIGTSTP,SIG_DFL);
597
598 set<int> KeepFDs;
599 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
600 if (Opts != 0 && Opts->Child != 0)
601 {
602 Opts = Opts->Child;
603 for (; Opts != 0; Opts = Opts->Next)
604 {
605 if (Opts->Value.empty() == true)
606 continue;
607 int fd = atoi(Opts->Value.c_str());
608 KeepFDs.insert(fd);
609 }
610 }
611
612 // Close all of our FDs - just in case
613 for (int K = 3; K != 40; K++)
614 {
615 if(KeepFDs.find(K) == KeepFDs.end())
616 fcntl(K,F_SETFD,FD_CLOEXEC);
617 }
618 }
619
620 return Process;
621 }
622 /*}}}*/
623 // ExecWait - Fancy waitpid /*{{{*/
624 // ---------------------------------------------------------------------
625 /* Waits for the given sub process. If Reap is set then no errors are
626 generated. Otherwise a failed subprocess will generate a proper descriptive
627 message */
628 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
629 {
630 if (Pid <= 1)
631 return true;
632
633 // Wait and collect the error code
634 int Status;
635 while (waitpid(Pid,&Status,0) != Pid)
636 {
637 if (errno == EINTR)
638 continue;
639
640 if (Reap == true)
641 return false;
642
643 return _error->Error(_("Waited for %s but it wasn't there"),Name);
644 }
645
646
647 // Check for an error code.
648 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
649 {
650 if (Reap == true)
651 return false;
652 if (WIFSIGNALED(Status) != 0)
653 {
654 if( WTERMSIG(Status) == SIGSEGV)
655 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
656 else
657 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
658 }
659
660 if (WIFEXITED(Status) != 0)
661 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
662
663 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
664 }
665
666 return true;
667 }
668 /*}}}*/
669
670 // FileFd::Open - Open a file /*{{{*/
671 // ---------------------------------------------------------------------
672 /* The most commonly used open mode combinations are given with Mode */
673 bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
674 {
675 Close();
676 Flags = AutoClose;
677 switch (Mode)
678 {
679 case ReadOnly:
680 iFd = open(FileName.c_str(),O_RDONLY);
681 break;
682
683 case ReadOnlyGzip:
684 iFd = open(FileName.c_str(),O_RDONLY);
685 if (iFd > 0) {
686 gz = gzdopen (iFd, "r");
687 if (gz == NULL) {
688 close (iFd);
689 iFd = -1;
690 }
691 }
692 break;
693
694 case WriteAtomic:
695 {
696 Flags |= Replace;
697 char *name = strdup((FileName + ".XXXXXX").c_str());
698 TemporaryFileName = string(mktemp(name));
699 iFd = open(TemporaryFileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
700 free(name);
701 break;
702 }
703
704 case WriteEmpty:
705 {
706 struct stat Buf;
707 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
708 unlink(FileName.c_str());
709 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
710 break;
711 }
712
713 case WriteExists:
714 iFd = open(FileName.c_str(),O_RDWR);
715 break;
716
717 case WriteAny:
718 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
719 break;
720
721 case WriteTemp:
722 unlink(FileName.c_str());
723 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
724 break;
725 }
726
727 if (iFd < 0)
728 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
729
730 this->FileName = FileName;
731 SetCloseExec(iFd,true);
732 return true;
733 }
734
735 bool FileFd::OpenDescriptor(int Fd, OpenMode Mode, bool AutoClose)
736 {
737 Close();
738 Flags = (AutoClose) ? FileFd::AutoClose : 0;
739 iFd = Fd;
740 if (Mode == ReadOnlyGzip) {
741 gz = gzdopen (iFd, "r");
742 if (gz == NULL) {
743 if (AutoClose)
744 close (iFd);
745 return _error->Errno("gzdopen",_("Could not open file descriptor %d"),
746 Fd);
747 }
748 }
749 this->FileName = "";
750 return true;
751 }
752 /*}}}*/
753 // FileFd::~File - Closes the file /*{{{*/
754 // ---------------------------------------------------------------------
755 /* If the proper modes are selected then we close the Fd and possibly
756 unlink the file on error. */
757 FileFd::~FileFd()
758 {
759 Close();
760 }
761 /*}}}*/
762 // FileFd::Read - Read a bit of the file /*{{{*/
763 // ---------------------------------------------------------------------
764 /* We are carefull to handle interruption by a signal while reading
765 gracefully. */
766 bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
767 {
768 int Res;
769 errno = 0;
770 if (Actual != 0)
771 *Actual = 0;
772
773 do
774 {
775 if (gz != NULL)
776 Res = gzread(gz,To,Size);
777 else
778 Res = read(iFd,To,Size);
779 if (Res < 0 && errno == EINTR)
780 continue;
781 if (Res < 0)
782 {
783 Flags |= Fail;
784 return _error->Errno("read",_("Read error"));
785 }
786
787 To = (char *)To + Res;
788 Size -= Res;
789 if (Actual != 0)
790 *Actual += Res;
791 }
792 while (Res > 0 && Size > 0);
793
794 if (Size == 0)
795 return true;
796
797 // Eof handling
798 if (Actual != 0)
799 {
800 Flags |= HitEof;
801 return true;
802 }
803
804 Flags |= Fail;
805 return _error->Error(_("read, still have %lu to read but none left"),Size);
806 }
807 /*}}}*/
808 // FileFd::Write - Write to the file /*{{{*/
809 // ---------------------------------------------------------------------
810 /* */
811 bool FileFd::Write(const void *From,unsigned long Size)
812 {
813 int Res;
814 errno = 0;
815 do
816 {
817 if (gz != NULL)
818 Res = gzwrite(gz,From,Size);
819 else
820 Res = write(iFd,From,Size);
821 if (Res < 0 && errno == EINTR)
822 continue;
823 if (Res < 0)
824 {
825 Flags |= Fail;
826 return _error->Errno("write",_("Write error"));
827 }
828
829 From = (char *)From + Res;
830 Size -= Res;
831 }
832 while (Res > 0 && Size > 0);
833
834 if (Size == 0)
835 return true;
836
837 Flags |= Fail;
838 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
839 }
840 /*}}}*/
841 // FileFd::Seek - Seek in the file /*{{{*/
842 // ---------------------------------------------------------------------
843 /* */
844 bool FileFd::Seek(unsigned long To)
845 {
846 int res;
847 if (gz)
848 res = gzseek(gz,To,SEEK_SET);
849 else
850 res = lseek(iFd,To,SEEK_SET);
851 if (res != (signed)To)
852 {
853 Flags |= Fail;
854 return _error->Error("Unable to seek to %lu",To);
855 }
856
857 return true;
858 }
859 /*}}}*/
860 // FileFd::Skip - Seek in the file /*{{{*/
861 // ---------------------------------------------------------------------
862 /* */
863 bool FileFd::Skip(unsigned long Over)
864 {
865 int res;
866 if (gz)
867 res = gzseek(gz,Over,SEEK_CUR);
868 else
869 res = lseek(iFd,Over,SEEK_CUR);
870 if (res < 0)
871 {
872 Flags |= Fail;
873 return _error->Error("Unable to seek ahead %lu",Over);
874 }
875
876 return true;
877 }
878 /*}}}*/
879 // FileFd::Truncate - Truncate the file /*{{{*/
880 // ---------------------------------------------------------------------
881 /* */
882 bool FileFd::Truncate(unsigned long To)
883 {
884 if (gz)
885 {
886 Flags |= Fail;
887 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
888 }
889 if (ftruncate(iFd,To) != 0)
890 {
891 Flags |= Fail;
892 return _error->Error("Unable to truncate to %lu",To);
893 }
894
895 return true;
896 }
897 /*}}}*/
898 // FileFd::Tell - Current seek position /*{{{*/
899 // ---------------------------------------------------------------------
900 /* */
901 unsigned long FileFd::Tell()
902 {
903 off_t Res;
904 if (gz)
905 Res = gztell(gz);
906 else
907 Res = lseek(iFd,0,SEEK_CUR);
908 if (Res == (off_t)-1)
909 _error->Errno("lseek","Failed to determine the current file position");
910 return Res;
911 }
912 /*}}}*/
913 // FileFd::Size - Return the size of the file /*{{{*/
914 // ---------------------------------------------------------------------
915 /* */
916 unsigned long FileFd::Size()
917 {
918 //TODO: For gz, do we need the actual file size here or the uncompressed length?
919 struct stat Buf;
920 if (fstat(iFd,&Buf) != 0)
921 return _error->Errno("fstat","Unable to determine the file size");
922 return Buf.st_size;
923 }
924 /*}}}*/
925 // FileFd::Close - Close the file if the close flag is set /*{{{*/
926 // ---------------------------------------------------------------------
927 /* */
928 bool FileFd::Close()
929 {
930 bool Res = true;
931 if ((Flags & AutoClose) == AutoClose)
932 {
933 if (gz != NULL) {
934 int const e = gzclose(gz);
935 // gzdopen() on empty files always fails with "buffer error" here, ignore that
936 if (e != 0 && e != Z_BUF_ERROR)
937 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
938 } else
939 if (iFd > 0 && close(iFd) != 0)
940 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
941 }
942
943 if ((Flags & Replace) == Replace && iFd >= 0) {
944 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
945 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
946
947 FileName = TemporaryFileName; // for the unlink() below.
948 }
949
950 iFd = -1;
951 gz = NULL;
952
953 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
954 FileName.empty() == false)
955 if (unlink(FileName.c_str()) != 0)
956 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
957
958
959 return Res;
960 }
961 /*}}}*/
962 // FileFd::Sync - Sync the file /*{{{*/
963 // ---------------------------------------------------------------------
964 /* */
965 bool FileFd::Sync()
966 {
967 #ifdef _POSIX_SYNCHRONIZED_IO
968 if (fsync(iFd) != 0)
969 return _error->Errno("sync",_("Problem syncing the file"));
970 #endif
971 return true;
972 }
973 /*}}}*/