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