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