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