1 //===-- FileSpec.cpp --------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 
11 #include "lldb/Host/File.h"
12 
13 #include <fcntl.h>
14 #include <limits.h>
15 #include <stdarg.h>
16 #include <sys/stat.h>
17 
18 #include "lldb/Core/Error.h"
19 #include "lldb/Host/Config.h"
20 #include "lldb/Host/FileSpec.h"
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 static const char *
26 GetStreamOpenModeFromOptions (uint32_t options)
27 {
28     if (options & File::eOpenOptionAppend)
29     {
30         if (options & File::eOpenOptionRead)
31         {
32             if (options & File::eOpenOptionCanCreateNewOnly)
33                 return "a+x";
34             else
35                 return "a+";
36         }
37         else if (options & File::eOpenOptionWrite)
38         {
39             if (options & File::eOpenOptionCanCreateNewOnly)
40                 return "ax";
41             else
42                 return "a";
43         }
44     }
45     else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite)
46     {
47         if (options & File::eOpenOptionCanCreate)
48         {
49             if (options & File::eOpenOptionCanCreateNewOnly)
50                 return "w+x";
51             else
52                 return "w+";
53         }
54         else
55             return "r+";
56     }
57     else if (options & File::eOpenOptionRead)
58     {
59         return "r";
60     }
61     else if (options & File::eOpenOptionWrite)
62     {
63         return "w";
64     }
65     return NULL;
66 }
67 
68 int File::kInvalidDescriptor = -1;
69 FILE * File::kInvalidStream = NULL;
70 
71 File::File(const char *path, uint32_t options, uint32_t permissions) :
72     m_descriptor (kInvalidDescriptor),
73     m_stream (kInvalidStream),
74     m_options (0),
75     m_owned (false)
76 {
77     Open (path, options, permissions);
78 }
79 
80 File::File (const File &rhs) :
81     m_descriptor (kInvalidDescriptor),
82     m_stream (kInvalidStream),
83     m_options (0),
84     m_owned (false)
85 {
86     Duplicate (rhs);
87 }
88 
89 
90 File &
91 File::operator = (const File &rhs)
92 {
93     if (this != &rhs)
94         Duplicate (rhs);
95     return *this;
96 }
97 
98 File::~File()
99 {
100     Close ();
101 }
102 
103 
104 int
105 File::GetDescriptor() const
106 {
107     if (DescriptorIsValid())
108         return m_descriptor;
109 
110     // Don't open the file descriptor if we don't need to, just get it from the
111     // stream if we have one.
112     if (StreamIsValid())
113         return fileno (m_stream);
114 
115     // Invalid descriptor and invalid stream, return invalid descriptor.
116     return kInvalidDescriptor;
117 }
118 
119 void
120 File::SetDescriptor (int fd, bool transfer_ownership)
121 {
122     if (IsValid())
123         Close();
124     m_descriptor = fd;
125     m_owned = transfer_ownership;
126 }
127 
128 
129 FILE *
130 File::GetStream ()
131 {
132     if (!StreamIsValid())
133     {
134         if (DescriptorIsValid())
135         {
136             const char *mode = GetStreamOpenModeFromOptions (m_options);
137             if (mode)
138                 m_stream = ::fdopen (m_descriptor, mode);
139         }
140     }
141     return m_stream;
142 }
143 
144 
145 void
146 File::SetStream (FILE *fh, bool transfer_ownership)
147 {
148     if (IsValid())
149         Close();
150     m_stream = fh;
151     m_owned = transfer_ownership;
152 }
153 
154 Error
155 File::Duplicate (const File &rhs)
156 {
157     Error error;
158     if (IsValid ())
159         Close();
160 
161     if (rhs.DescriptorIsValid())
162     {
163         m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD);
164         if (!DescriptorIsValid())
165             error.SetErrorToErrno();
166         else
167         {
168             m_options = rhs.m_options;
169             m_owned = true;
170         }
171     }
172     else
173     {
174         error.SetErrorString ("invalid file to duplicate");
175     }
176     return error;
177 }
178 
179 Error
180 File::Open (const char *path, uint32_t options, uint32_t permissions)
181 {
182     Error error;
183     if (IsValid())
184         Close ();
185 
186     int oflag = 0;
187     if (options & eOpenOptionRead &&
188         options & eOpenOptionWrite )
189         oflag |= O_RDWR;
190     else if (options & eOpenOptionRead)
191         oflag |= O_RDONLY;
192     else if (options & eOpenOptionWrite)
193         oflag |= O_WRONLY;
194 
195     if (options & eOpenOptionNonBlocking)
196         oflag |= O_NONBLOCK;
197 
198     if (options & eOpenOptionAppend)
199         oflag |= O_APPEND;
200     else
201         oflag |= O_TRUNC;
202 
203     if (options & eOpenOptionCanCreate)
204         oflag |= O_CREAT;
205 
206     if (options & eOpenOptionCanCreateNewOnly)
207         oflag |= O_CREAT | O_EXCL;
208 
209     mode_t mode = 0;
210     if (permissions & ePermissionsUserRead)     mode |= S_IRUSR;
211     if (permissions & ePermissionsUserWrite)    mode |= S_IWUSR;
212     if (permissions & ePermissionsUserExecute)  mode |= S_IXUSR;
213     if (permissions & ePermissionsGroupRead)    mode |= S_IRGRP;
214     if (permissions & ePermissionsGroupWrite)   mode |= S_IWGRP;
215     if (permissions & ePermissionsGroupExecute) mode |= S_IXGRP;
216     if (permissions & ePermissionsWorldRead)    mode |= S_IROTH;
217     if (permissions & ePermissionsWorldWrite)   mode |= S_IWOTH;
218     if (permissions & ePermissionsWorldExecute) mode |= S_IXOTH;
219 
220     m_descriptor = ::open(path, oflag, mode);
221     if (!DescriptorIsValid())
222         error.SetErrorToErrno();
223     else
224         m_owned = true;
225 
226     return error;
227 }
228 
229 Error
230 File::Close ()
231 {
232     Error error;
233     if (IsValid ())
234     {
235         if (m_owned)
236         {
237             if (StreamIsValid())
238             {
239                 if (::fclose (m_stream) == EOF)
240                     error.SetErrorToErrno();
241             }
242 
243             if (DescriptorIsValid())
244             {
245                 if (::close (m_descriptor) != 0)
246                     error.SetErrorToErrno();
247             }
248         }
249         m_descriptor = kInvalidDescriptor;
250         m_stream = kInvalidStream;
251         m_options = 0;
252         m_owned = false;
253     }
254     return error;
255 }
256 
257 
258 Error
259 File::GetFileSpec (FileSpec &file_spec) const
260 {
261     Error error;
262 #ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED
263     if (IsValid ())
264     {
265         char path[PATH_MAX];
266         if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
267             error.SetErrorToErrno();
268         else
269             file_spec.SetFile (path, false);
270     }
271     else
272     {
273         error.SetErrorString("invalid file handle");
274     }
275 #elif defined(__linux__)
276     char proc[64];
277     char path[PATH_MAX];
278     if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
279         error.SetErrorString ("Cannot resolve file descriptor\n");
280     else
281     {
282         ssize_t len;
283         if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
284             error.SetErrorToErrno();
285         else
286         {
287             path[len] = '\0';
288             file_spec.SetFile (path, false);
289         }
290     }
291 #else
292     error.SetErrorString ("File::GetFileSpec is not supported on this platform");
293 #endif
294 
295     if (error.Fail())
296         file_spec.Clear();
297     return error;
298 }
299 
300 Error
301 File::SeekFromStart (off_t& offset)
302 {
303     Error error;
304     if (DescriptorIsValid())
305     {
306         offset = ::lseek (m_descriptor, offset, SEEK_SET);
307 
308         if (offset == -1)
309             error.SetErrorToErrno();
310     }
311     else
312     {
313         error.SetErrorString("invalid file handle");
314     }
315     return error;
316 }
317 
318 Error
319 File::SeekFromCurrent (off_t& offset)
320 {
321     Error error;
322     if (DescriptorIsValid())
323     {
324         offset = ::lseek (m_descriptor, offset, SEEK_CUR);
325 
326         if (offset == -1)
327             error.SetErrorToErrno();
328     }
329     else
330     {
331         error.SetErrorString("invalid file handle");
332     }
333     return error;
334 }
335 
336 Error
337 File::SeekFromEnd (off_t& offset)
338 {
339     Error error;
340     if (DescriptorIsValid())
341     {
342         offset = ::lseek (m_descriptor, offset, SEEK_CUR);
343 
344         if (offset == -1)
345             error.SetErrorToErrno();
346     }
347     else
348     {
349         error.SetErrorString("invalid file handle");
350     }
351     return error;
352 }
353 
354 Error
355 File::Flush ()
356 {
357     Error error;
358     if (StreamIsValid())
359     {
360         if (::fflush (m_stream) == EOF)
361             error.SetErrorToErrno();
362     }
363     else if (!DescriptorIsValid())
364     {
365         error.SetErrorString("invalid file handle");
366     }
367     return error;
368 }
369 
370 
371 Error
372 File::Sync ()
373 {
374     Error error;
375     if (DescriptorIsValid())
376     {
377         if (::fsync (m_descriptor) == -1)
378             error.SetErrorToErrno();
379     }
380     else
381     {
382         error.SetErrorString("invalid file handle");
383     }
384     return error;
385 }
386 
387 Error
388 File::Read (void *buf, size_t &num_bytes)
389 {
390     Error error;
391     if (DescriptorIsValid())
392     {
393         ssize_t bytes_read = ::read (m_descriptor, buf, num_bytes);
394         if (bytes_read == -1)
395         {
396             error.SetErrorToErrno();
397             num_bytes = 0;
398         }
399         else
400             num_bytes = bytes_read;
401     }
402     else if (StreamIsValid())
403     {
404         size_t bytes_read = ::fread (buf, 1, num_bytes, m_stream);
405         if (bytes_read == 0)
406         {
407             if (::feof(m_stream))
408                 error.SetErrorString ("feof");
409             else if (::ferror (m_stream))
410                 error.SetErrorString ("ferror");
411             num_bytes = 0;
412         }
413         else
414             num_bytes = bytes_read;
415     }
416     else
417     {
418         num_bytes = 0;
419         error.SetErrorString("invalid file handle");
420     }
421     return error;
422 }
423 
424 Error
425 File::Write (const void *buf, size_t &num_bytes)
426 {
427     Error error;
428     if (DescriptorIsValid())
429     {
430         ssize_t bytes_written = ::write (m_descriptor, buf, num_bytes);
431         if (bytes_written == -1)
432         {
433             error.SetErrorToErrno();
434             num_bytes = 0;
435         }
436         else
437             num_bytes = bytes_written;
438     }
439     else if (StreamIsValid())
440     {
441         size_t bytes_written = ::fwrite (buf, 1, num_bytes, m_stream);
442         if (bytes_written == 0)
443         {
444             if (::feof(m_stream))
445                 error.SetErrorString ("feof");
446             else if (::ferror (m_stream))
447                 error.SetErrorString ("ferror");
448             num_bytes = 0;
449         }
450         else
451             num_bytes = bytes_written;
452 
453     }
454     else
455     {
456         num_bytes = 0;
457         error.SetErrorString("invalid file handle");
458     }
459     return error;
460 }
461 
462 
463 Error
464 File::Read (void *buf, size_t &num_bytes, off_t &offset)
465 {
466     Error error;
467     int fd = GetDescriptor();
468     if (fd != kInvalidDescriptor)
469     {
470         ssize_t bytes_read = ::pread (fd, buf, num_bytes, offset);
471         if (bytes_read < 0)
472         {
473             num_bytes = 0;
474             error.SetErrorToErrno();
475         }
476         else
477         {
478             offset += bytes_read;
479             num_bytes = bytes_read;
480         }
481     }
482     else
483     {
484         num_bytes = 0;
485         error.SetErrorString("invalid file handle");
486     }
487     return error;
488 }
489 
490 Error
491 File::Write (const void *buf, size_t &num_bytes, off_t &offset)
492 {
493     Error error;
494     int fd = GetDescriptor();
495     if (fd != kInvalidDescriptor)
496     {
497         ssize_t bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset);
498         if (bytes_written < 0)
499         {
500             num_bytes = 0;
501             error.SetErrorToErrno();
502         }
503         else
504         {
505             offset += bytes_written;
506             num_bytes = bytes_written;
507         }
508     }
509     else
510     {
511         num_bytes = 0;
512         error.SetErrorString("invalid file handle");
513     }
514     return error;
515 }
516 
517 //------------------------------------------------------------------
518 // Print some formatted output to the stream.
519 //------------------------------------------------------------------
520 int
521 File::Printf (const char *format, ...)
522 {
523     va_list args;
524     va_start (args, format);
525     int result = PrintfVarArg (format, args);
526     va_end (args);
527     return result;
528 }
529 
530 //------------------------------------------------------------------
531 // Print some formatted output to the stream.
532 //------------------------------------------------------------------
533 int
534 File::PrintfVarArg (const char *format, va_list args)
535 {
536     int result = 0;
537     if (DescriptorIsValid())
538     {
539         char *s = NULL;
540         result = vasprintf(&s, format, args);
541         if (s != NULL)
542         {
543             if (result > 0)
544             {
545                 size_t s_len = result;
546                 Write (s, s_len);
547                 result = s_len;
548             }
549             free (s);
550         }
551     }
552     else if (StreamIsValid())
553     {
554         result = ::vfprintf (m_stream, format, args);
555     }
556     return result;
557 }
558