]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
Purge support
[apt.git] / apt-pkg / contrib / fileutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: fileutl.cc,v 1.27 1999/04/20 05:02:09 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.
13
14 ##################################################################### */
15 /*}}}*/
16 // Include Files /*{{{*/
17 #ifdef __GNUG__
18 #pragma implementation "apt-pkg/fileutl.h"
19 #endif
20 #include <apt-pkg/fileutl.h>
21 #include <apt-pkg/error.h>
22
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/fcntl.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <signal.h>
29 #include <errno.h>
30 /*}}}*/
31
32 // CopyFile - Buffered copy of a file /*{{{*/
33 // ---------------------------------------------------------------------
34 /* The caller is expected to set things so that failure causes erasure */
35 bool CopyFile(FileFd &From,FileFd &To)
36 {
37 if (From.IsOpen() == false || To.IsOpen() == false)
38 return false;
39
40 // Buffered copy between fds
41 unsigned char *Buf = new unsigned char[64000];
42 unsigned long Size = From.Size();
43 while (Size != 0)
44 {
45 unsigned long ToRead = Size;
46 if (Size > 64000)
47 ToRead = 64000;
48
49 if (From.Read(Buf,ToRead) == false ||
50 To.Write(Buf,ToRead) == false)
51 {
52 delete [] Buf;
53 return false;
54 }
55
56 Size -= ToRead;
57 }
58
59 delete [] Buf;
60 return true;
61 }
62 /*}}}*/
63 // GetLock - Gets a lock file /*{{{*/
64 // ---------------------------------------------------------------------
65 /* This will create an empty file of the given name and lock it. Once this
66 is done all other calls to GetLock in any other process will fail with
67 -1. The return result is the fd of the file, the call should call
68 close at some time. */
69 int GetLock(string File,bool Errors)
70 {
71 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_TRUNC,0640);
72 if (FD < 0)
73 {
74 if (Errors == true)
75 _error->Errno("open","Could not open lock file %s",File.c_str());
76 return -1;
77 }
78
79 // Aquire a write lock
80 struct flock fl;
81 fl.l_type = F_WRLCK;
82 fl.l_whence = SEEK_SET;
83 fl.l_start = 0;
84 fl.l_len = 0;
85 if (fcntl(FD,F_SETLK,&fl) == -1)
86 {
87 if (errno == ENOLCK)
88 {
89 _error->Warning("Not using locking for nfs mounted lock file %s",File.c_str());
90 return true;
91 }
92 if (Errors == true)
93 _error->Errno("open","Could not get lock %s",File.c_str());
94 close(FD);
95 return -1;
96 }
97
98 return FD;
99 }
100 /*}}}*/
101 // FileExists - Check if a file exists /*{{{*/
102 // ---------------------------------------------------------------------
103 /* */
104 bool FileExists(string File)
105 {
106 struct stat Buf;
107 if (stat(File.c_str(),&Buf) != 0)
108 return false;
109 return true;
110 }
111 /*}}}*/
112 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
113 // ---------------------------------------------------------------------
114 /* We return / on failure. */
115 string SafeGetCWD()
116 {
117 // Stash the current dir.
118 char S[300];
119 S[0] = 0;
120 if (getcwd(S,sizeof(S)-2) == 0)
121 return "/";
122 unsigned int Len = strlen(S);
123 S[Len] = '/';
124 S[Len+1] = 0;
125 return S;
126 }
127 /*}}}*/
128 // flNotDir - Strip the directory from the filename /*{{{*/
129 // ---------------------------------------------------------------------
130 /* */
131 string flNotDir(string File)
132 {
133 string::size_type Res = File.rfind('/');
134 if (Res == string::npos)
135 return File;
136 Res++;
137 return string(File,Res,Res - File.length());
138 }
139 /*}}}*/
140 // flNotFile - Strip the file from the directory name /*{{{*/
141 // ---------------------------------------------------------------------
142 /* */
143 string flNotFile(string File)
144 {
145 string::size_type Res = File.rfind('/');
146 if (Res == string::npos)
147 return File;
148 Res++;
149 return string(File,0,Res);
150 }
151 /*}}}*/
152 // SetCloseExec - Set the close on exec flag /*{{{*/
153 // ---------------------------------------------------------------------
154 /* */
155 void SetCloseExec(int Fd,bool Close)
156 {
157 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
158 {
159 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
160 exit(100);
161 }
162 }
163 /*}}}*/
164 // SetNonBlock - Set the nonblocking flag /*{{{*/
165 // ---------------------------------------------------------------------
166 /* */
167 void SetNonBlock(int Fd,bool Block)
168 {
169 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
170 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
171 {
172 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
173 exit(100);
174 }
175 }
176 /*}}}*/
177 // WaitFd - Wait for a FD to become readable /*{{{*/
178 // ---------------------------------------------------------------------
179 /* This waits for a FD to become readable using select. It is usefull for
180 applications making use of non-blocking sockets. The timeout is
181 in seconds. */
182 bool WaitFd(int Fd,bool write,unsigned long timeout)
183 {
184 fd_set Set;
185 struct timeval tv;
186 FD_ZERO(&Set);
187 FD_SET(Fd,&Set);
188 tv.tv_sec = timeout;
189 tv.tv_usec = 0;
190 if (write == true)
191 {
192 int Res;
193 do
194 {
195 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
196 }
197 while (Res < 0 && errno == EINTR);
198
199 if (Res <= 0)
200 return false;
201 }
202 else
203 {
204 int Res;
205 do
206 {
207 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
208 }
209 while (Res < 0 && errno == EINTR);
210
211 if (Res <= 0)
212 return false;
213 }
214
215 return true;
216 }
217 /*}}}*/
218 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
219 // ---------------------------------------------------------------------
220 /* This is used if you want to cleanse the environment for the forked
221 child, it fixes up the important signals and nukes all of the fds,
222 otherwise acts like normal fork. */
223 int ExecFork()
224 {
225 // Fork off the process
226 pid_t Process = fork();
227 if (Process < 0)
228 {
229 cerr << "FATAL -> Failed to fork." << endl;
230 exit(100);
231 }
232
233 // Spawn the subprocess
234 if (Process == 0)
235 {
236 // Setup the signals
237 signal(SIGPIPE,SIG_DFL);
238 signal(SIGQUIT,SIG_DFL);
239 signal(SIGINT,SIG_DFL);
240 signal(SIGWINCH,SIG_DFL);
241 signal(SIGCONT,SIG_DFL);
242 signal(SIGTSTP,SIG_DFL);
243
244 // Close all of our FDs - just in case
245 for (int K = 3; K != 40; K++)
246 fcntl(K,F_SETFD,FD_CLOEXEC);
247 }
248
249 return Process;
250 }
251 /*}}}*/
252
253 // FileFd::FileFd - Open a file /*{{{*/
254 // ---------------------------------------------------------------------
255 /* The most commonly used open mode combinations are given with Mode */
256 FileFd::FileFd(string FileName,OpenMode Mode, unsigned long Perms)
257 {
258 Flags = AutoClose;
259 switch (Mode)
260 {
261 case ReadOnly:
262 iFd = open(FileName.c_str(),O_RDONLY);
263 break;
264
265 case WriteEmpty:
266 {
267 struct stat Buf;
268 if (stat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
269 unlink(FileName.c_str());
270 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
271 break;
272 }
273
274 case WriteExists:
275 iFd = open(FileName.c_str(),O_RDWR);
276 break;
277
278 case WriteAny:
279 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
280 break;
281 }
282
283 if (iFd < 0)
284 _error->Errno("open","Could not open file %s",FileName.c_str());
285 else
286 {
287 this->FileName = FileName;
288 SetCloseExec(iFd,true);
289 }
290 }
291 /*}}}*/
292 // FileFd::~File - Closes the file /*{{{*/
293 // ---------------------------------------------------------------------
294 /* If the proper modes are selected then we close the Fd and possibly
295 unlink the file on error. */
296 FileFd::~FileFd()
297 {
298 Close();
299 }
300 /*}}}*/
301 // FileFd::Read - Read a bit of the file /*{{{*/
302 // ---------------------------------------------------------------------
303 /* We are carefull to handle interruption by a signal while reading
304 gracefully. */
305 bool FileFd::Read(void *To,unsigned long Size)
306 {
307 int Res;
308 errno = 0;
309 do
310 {
311 Res = read(iFd,To,Size);
312 if (Res < 0 && errno == EINTR)
313 continue;
314 if (Res < 0)
315 {
316 Flags |= Fail;
317 return _error->Errno("read","Read error");
318 }
319
320 To = (char *)To + Res;
321 Size -= Res;
322 }
323 while (Res > 0 && Size > 0);
324
325 if (Size == 0)
326 return true;
327
328 Flags |= Fail;
329 return _error->Error("read, still have %u to read but none left",Size);
330 }
331 /*}}}*/
332 // FileFd::Write - Write to the file /*{{{*/
333 // ---------------------------------------------------------------------
334 /* */
335 bool FileFd::Write(const void *From,unsigned long Size)
336 {
337 int Res;
338 errno = 0;
339 do
340 {
341 Res = write(iFd,From,Size);
342 if (Res < 0 && errno == EINTR)
343 continue;
344 if (Res < 0)
345 {
346 Flags |= Fail;
347 return _error->Errno("write","Write error");
348 }
349
350 From = (char *)From + Res;
351 Size -= Res;
352 }
353 while (Res > 0 && Size > 0);
354
355 if (Size == 0)
356 return true;
357
358 Flags |= Fail;
359 return _error->Error("write, still have %u to write but couldn't",Size);
360 }
361 /*}}}*/
362 // FileFd::Seek - Seek in the file /*{{{*/
363 // ---------------------------------------------------------------------
364 /* */
365 bool FileFd::Seek(unsigned long To)
366 {
367 if (lseek(iFd,To,SEEK_SET) != (signed)To)
368 {
369 Flags |= Fail;
370 return _error->Error("Unable to seek to %u",To);
371 }
372
373 return true;
374 }
375 /*}}}*/
376 // FileFd::Truncate - Truncate the file /*{{{*/
377 // ---------------------------------------------------------------------
378 /* */
379 bool FileFd::Truncate(unsigned long To)
380 {
381 if (ftruncate(iFd,To) != 0)
382 {
383 Flags |= Fail;
384 return _error->Error("Unable to truncate to %u",To);
385 }
386
387 return true;
388 }
389 /*}}}*/
390 // FileFd::Tell - Current seek position /*{{{*/
391 // ---------------------------------------------------------------------
392 /* */
393 unsigned long FileFd::Tell()
394 {
395 off_t Res = lseek(iFd,0,SEEK_CUR);
396 if (Res == (off_t)-1)
397 _error->Errno("lseek","Failed to determine the current file position");
398 return Res;
399 }
400 /*}}}*/
401 // FileFd::Size - Return the size of the file /*{{{*/
402 // ---------------------------------------------------------------------
403 /* */
404 unsigned long FileFd::Size()
405 {
406 struct stat Buf;
407 if (fstat(iFd,&Buf) != 0)
408 return _error->Errno("fstat","Unable to determine the file size");
409 return Buf.st_size;
410 }
411 /*}}}*/
412 // FileFd::Close - Close the file if the close flag is set /*{{{*/
413 // ---------------------------------------------------------------------
414 /* */
415 bool FileFd::Close()
416 {
417 bool Res = true;
418 if ((Flags & AutoClose) == AutoClose)
419 if (iFd >= 0 && close(iFd) != 0)
420 Res &= _error->Errno("close","Problem closing the file");
421 iFd = -1;
422
423 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
424 FileName.empty() == false)
425 if (unlink(FileName.c_str()) != 0)
426 Res &= _error->Warning("unlnk","Problem unlinking the file");
427 return Res;
428 }
429 /*}}}*/