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