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 <errno.h>
14#include <fcntl.h>
15#include <limits.h>
16#include <stdarg.h>
17#include <sys/stat.h>
18
19#include "lldb/Core/DataBufferHeap.h"
20#include "lldb/Core/Error.h"
21#include "lldb/Host/Config.h"
22#include "lldb/Host/FileSpec.h"
23
24using namespace lldb;
25using namespace lldb_private;
26
27static const char *
28GetStreamOpenModeFromOptions (uint32_t options)
29{
30    if (options & File::eOpenOptionAppend)
31    {
32        if (options & File::eOpenOptionRead)
33        {
34            if (options & File::eOpenOptionCanCreateNewOnly)
35                return "a+x";
36            else
37                return "a+";
38        }
39        else if (options & File::eOpenOptionWrite)
40        {
41            if (options & File::eOpenOptionCanCreateNewOnly)
42                return "ax";
43            else
44                return "a";
45        }
46    }
47    else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite)
48    {
49        if (options & File::eOpenOptionCanCreate)
50        {
51            if (options & File::eOpenOptionCanCreateNewOnly)
52                return "w+x";
53            else
54                return "w+";
55        }
56        else
57            return "r+";
58    }
59    else if (options & File::eOpenOptionRead)
60    {
61        return "r";
62    }
63    else if (options & File::eOpenOptionWrite)
64    {
65        return "w";
66    }
67    return NULL;
68}
69
70int File::kInvalidDescriptor = -1;
71FILE * File::kInvalidStream = NULL;
72
73File::File(const char *path, uint32_t options, uint32_t permissions) :
74    m_descriptor (kInvalidDescriptor),
75    m_stream (kInvalidStream),
76    m_options (0),
77    m_owned (false)
78{
79    Open (path, options, permissions);
80}
81
82File::File (const File &rhs) :
83    m_descriptor (kInvalidDescriptor),
84    m_stream (kInvalidStream),
85    m_options (0),
86    m_owned (false)
87{
88    Duplicate (rhs);
89}
90
91
92File &
93File::operator = (const File &rhs)
94{
95    if (this != &rhs)
96        Duplicate (rhs);
97    return *this;
98}
99
100File::~File()
101{
102    Close ();
103}
104
105
106int
107File::GetDescriptor() const
108{
109    if (DescriptorIsValid())
110        return m_descriptor;
111
112    // Don't open the file descriptor if we don't need to, just get it from the
113    // stream if we have one.
114    if (StreamIsValid())
115        return fileno (m_stream);
116
117    // Invalid descriptor and invalid stream, return invalid descriptor.
118    return kInvalidDescriptor;
119}
120
121void
122File::SetDescriptor (int fd, bool transfer_ownership)
123{
124    if (IsValid())
125        Close();
126    m_descriptor = fd;
127    m_owned = transfer_ownership;
128}
129
130
131FILE *
132File::GetStream ()
133{
134    if (!StreamIsValid())
135    {
136        if (DescriptorIsValid())
137        {
138            const char *mode = GetStreamOpenModeFromOptions (m_options);
139            if (mode)
140            {
141                do
142                {
143                    m_stream = ::fdopen (m_descriptor, mode);
144                } while (m_stream == NULL && errno == EINTR);
145            }
146        }
147    }
148    return m_stream;
149}
150
151
152void
153File::SetStream (FILE *fh, bool transfer_ownership)
154{
155    if (IsValid())
156        Close();
157    m_stream = fh;
158    m_owned = transfer_ownership;
159}
160
161Error
162File::Duplicate (const File &rhs)
163{
164    Error error;
165    if (IsValid ())
166        Close();
167
168    if (rhs.DescriptorIsValid())
169    {
170        m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD);
171        if (!DescriptorIsValid())
172            error.SetErrorToErrno();
173        else
174        {
175            m_options = rhs.m_options;
176            m_owned = true;
177        }
178    }
179    else
180    {
181        error.SetErrorString ("invalid file to duplicate");
182    }
183    return error;
184}
185
186Error
187File::Open (const char *path, uint32_t options, uint32_t permissions)
188{
189    Error error;
190    if (IsValid())
191        Close ();
192
193    int oflag = 0;
194    const bool read = options & eOpenOptionRead;
195    const bool write = options & eOpenOptionWrite;
196    if (write)
197    {
198        if (read)
199            oflag |= O_RDWR;
200        else
201            oflag |= O_WRONLY;
202
203        if (options & eOpenOptionAppend)
204            oflag |= O_APPEND;
205
206        if (options & eOpenOptionTruncate)
207            oflag |= O_TRUNC;
208
209        if (options & eOpenOptionCanCreate)
210            oflag |= O_CREAT;
211
212        if (options & eOpenOptionCanCreateNewOnly)
213            oflag |= O_CREAT | O_EXCL;
214    }
215    else if (read)
216    {
217        oflag |= O_RDONLY;
218    }
219
220    if (options & eOpenOptionNonBlocking)
221        oflag |= O_NONBLOCK;
222
223    mode_t mode = 0;
224    if (oflag & O_CREAT)
225    {
226        if (permissions & ePermissionsUserRead)     mode |= S_IRUSR;
227        if (permissions & ePermissionsUserWrite)    mode |= S_IWUSR;
228        if (permissions & ePermissionsUserExecute)  mode |= S_IXUSR;
229        if (permissions & ePermissionsGroupRead)    mode |= S_IRGRP;
230        if (permissions & ePermissionsGroupWrite)   mode |= S_IWGRP;
231        if (permissions & ePermissionsGroupExecute) mode |= S_IXGRP;
232        if (permissions & ePermissionsWorldRead)    mode |= S_IROTH;
233        if (permissions & ePermissionsWorldWrite)   mode |= S_IWOTH;
234        if (permissions & ePermissionsWorldExecute) mode |= S_IXOTH;
235    }
236
237    do
238    {
239        m_descriptor = ::open(path, oflag, mode);
240    } while (m_descriptor < 0 && errno == EINTR);
241
242    if (!DescriptorIsValid())
243        error.SetErrorToErrno();
244    else
245        m_owned = true;
246
247    return error;
248}
249
250Error
251File::Close ()
252{
253    Error error;
254    if (IsValid ())
255    {
256        if (m_owned)
257        {
258            if (StreamIsValid())
259            {
260                if (::fclose (m_stream) == EOF)
261                    error.SetErrorToErrno();
262            }
263
264            if (DescriptorIsValid())
265            {
266                if (::close (m_descriptor) != 0)
267                    error.SetErrorToErrno();
268            }
269        }
270        m_descriptor = kInvalidDescriptor;
271        m_stream = kInvalidStream;
272        m_options = 0;
273        m_owned = false;
274    }
275    return error;
276}
277
278
279Error
280File::GetFileSpec (FileSpec &file_spec) const
281{
282    Error error;
283#ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED
284    if (IsValid ())
285    {
286        char path[PATH_MAX];
287        if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
288            error.SetErrorToErrno();
289        else
290            file_spec.SetFile (path, false);
291    }
292    else
293    {
294        error.SetErrorString("invalid file handle");
295    }
296#elif defined(__linux__)
297    char proc[64];
298    char path[PATH_MAX];
299    if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
300        error.SetErrorString ("cannot resolve file descriptor");
301    else
302    {
303        ssize_t len;
304        if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
305            error.SetErrorToErrno();
306        else
307        {
308            path[len] = '\0';
309            file_spec.SetFile (path, false);
310        }
311    }
312#else
313    error.SetErrorString ("File::GetFileSpec is not supported on this platform");
314#endif
315
316    if (error.Fail())
317        file_spec.Clear();
318    return error;
319}
320
321off_t
322File::SeekFromStart (off_t offset, Error *error_ptr)
323{
324    off_t result = 0;
325    if (DescriptorIsValid())
326    {
327        result = ::lseek (m_descriptor, offset, SEEK_SET);
328
329        if (error_ptr)
330        {
331            if (result == -1)
332                error_ptr->SetErrorToErrno();
333            else
334                error_ptr->Clear();
335        }
336    }
337    else if (StreamIsValid ())
338    {
339        result = ::fseek(m_stream, offset, SEEK_SET);
340
341        if (error_ptr)
342        {
343            if (result == -1)
344                error_ptr->SetErrorToErrno();
345            else
346                error_ptr->Clear();
347        }
348    }
349    else if (error_ptr)
350    {
351        error_ptr->SetErrorString("invalid file handle");
352    }
353    return result;
354}
355
356off_t
357File::SeekFromCurrent (off_t offset,  Error *error_ptr)
358{
359    off_t result = -1;
360    if (DescriptorIsValid())
361    {
362        result = ::lseek (m_descriptor, offset, SEEK_CUR);
363
364        if (error_ptr)
365        {
366            if (result == -1)
367                error_ptr->SetErrorToErrno();
368            else
369                error_ptr->Clear();
370        }
371    }
372    else if (StreamIsValid ())
373    {
374        result = ::fseek(m_stream, offset, SEEK_CUR);
375
376        if (error_ptr)
377        {
378            if (result == -1)
379                error_ptr->SetErrorToErrno();
380            else
381                error_ptr->Clear();
382        }
383    }
384    else if (error_ptr)
385    {
386        error_ptr->SetErrorString("invalid file handle");
387    }
388    return result;
389}
390
391off_t
392File::SeekFromEnd (off_t offset, Error *error_ptr)
393{
394    off_t result = -1;
395    if (DescriptorIsValid())
396    {
397        result = ::lseek (m_descriptor, offset, SEEK_END);
398
399        if (error_ptr)
400        {
401            if (result == -1)
402                error_ptr->SetErrorToErrno();
403            else
404                error_ptr->Clear();
405        }
406    }
407    else if (StreamIsValid ())
408    {
409        result = ::fseek(m_stream, offset, SEEK_END);
410
411        if (error_ptr)
412        {
413            if (result == -1)
414                error_ptr->SetErrorToErrno();
415            else
416                error_ptr->Clear();
417        }
418    }
419    else if (error_ptr)
420    {
421        error_ptr->SetErrorString("invalid file handle");
422    }
423    return result;
424}
425
426Error
427File::Flush ()
428{
429    Error error;
430    if (StreamIsValid())
431    {
432        int err = 0;
433        do
434        {
435            err = ::fflush (m_stream);
436        } while (err == EOF && errno == EINTR);
437
438        if (err == EOF)
439            error.SetErrorToErrno();
440    }
441    else if (!DescriptorIsValid())
442    {
443        error.SetErrorString("invalid file handle");
444    }
445    return error;
446}
447
448
449Error
450File::Sync ()
451{
452    Error error;
453    if (DescriptorIsValid())
454    {
455        int err = 0;
456        do
457        {
458            err = ::fsync (m_descriptor);
459        } while (err == -1 && errno == EINTR);
460
461        if (err == -1)
462            error.SetErrorToErrno();
463    }
464    else
465    {
466        error.SetErrorString("invalid file handle");
467    }
468    return error;
469}
470
471Error
472File::Read (void *buf, size_t &num_bytes)
473{
474    Error error;
475    ssize_t bytes_read = -1;
476    if (DescriptorIsValid())
477    {
478        do
479        {
480            bytes_read = ::read (m_descriptor, buf, num_bytes);
481        } while (bytes_read < 0 && errno == EINTR);
482
483        if (bytes_read == -1)
484        {
485            error.SetErrorToErrno();
486            num_bytes = 0;
487        }
488        else
489            num_bytes = bytes_read;
490    }
491    else if (StreamIsValid())
492    {
493        bytes_read = ::fread (buf, 1, num_bytes, m_stream);
494
495        if (bytes_read == 0)
496        {
497            if (::feof(m_stream))
498                error.SetErrorString ("feof");
499            else if (::ferror (m_stream))
500                error.SetErrorString ("ferror");
501            num_bytes = 0;
502        }
503        else
504            num_bytes = bytes_read;
505    }
506    else
507    {
508        num_bytes = 0;
509        error.SetErrorString("invalid file handle");
510    }
511    return error;
512}
513
514Error
515File::Write (const void *buf, size_t &num_bytes)
516{
517    Error error;
518    ssize_t bytes_written = -1;
519    if (DescriptorIsValid())
520    {
521        do
522        {
523            bytes_written = ::write (m_descriptor, buf, num_bytes);
524        } while (bytes_written < 0 && errno == EINTR);
525
526        if (bytes_written == -1)
527        {
528            error.SetErrorToErrno();
529            num_bytes = 0;
530        }
531        else
532            num_bytes = bytes_written;
533    }
534    else if (StreamIsValid())
535    {
536        bytes_written = ::fwrite (buf, 1, num_bytes, m_stream);
537
538        if (bytes_written == 0)
539        {
540            if (::feof(m_stream))
541                error.SetErrorString ("feof");
542            else if (::ferror (m_stream))
543                error.SetErrorString ("ferror");
544            num_bytes = 0;
545        }
546        else
547            num_bytes = bytes_written;
548
549    }
550    else
551    {
552        num_bytes = 0;
553        error.SetErrorString("invalid file handle");
554    }
555    return error;
556}
557
558
559Error
560File::Read (void *buf, size_t &num_bytes, off_t &offset)
561{
562    Error error;
563    int fd = GetDescriptor();
564    if (fd != kInvalidDescriptor)
565    {
566        ssize_t bytes_read = -1;
567        do
568        {
569            bytes_read = ::pread (fd, buf, num_bytes, offset);
570        } while (bytes_read < 0 && errno == EINTR);
571
572        if (bytes_read < 0)
573        {
574            num_bytes = 0;
575            error.SetErrorToErrno();
576        }
577        else
578        {
579            offset += bytes_read;
580            num_bytes = bytes_read;
581        }
582    }
583    else
584    {
585        num_bytes = 0;
586        error.SetErrorString("invalid file handle");
587    }
588    return error;
589}
590
591Error
592File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp)
593{
594    Error error;
595
596    if (num_bytes > 0)
597    {
598        int fd = GetDescriptor();
599        if (fd != kInvalidDescriptor)
600        {
601            struct stat file_stats;
602            if (::fstat (fd, &file_stats) == 0)
603            {
604                if (file_stats.st_size > offset)
605                {
606                    const size_t bytes_left = file_stats.st_size - offset;
607                    if (num_bytes > bytes_left)
608                        num_bytes = bytes_left;
609
610                    std::unique_ptr<DataBufferHeap> data_heap_ap;
611                    data_heap_ap.reset(new DataBufferHeap(num_bytes + (null_terminate ? 1 : 0), '\0'));
612
613                    if (data_heap_ap.get())
614                    {
615                        error = Read (data_heap_ap->GetBytes(), num_bytes, offset);
616                        if (error.Success())
617                        {
618                            // Make sure we read exactly what we asked for and if we got
619                            // less, adjust the array
620                            if (num_bytes < data_heap_ap->GetByteSize())
621                                data_heap_ap->SetByteSize(num_bytes);
622                            data_buffer_sp.reset(data_heap_ap.release());
623                            return error;
624                        }
625                    }
626                }
627                else
628                    error.SetErrorString("file is empty");
629            }
630            else
631                error.SetErrorToErrno();
632        }
633        else
634            error.SetErrorString("invalid file handle");
635    }
636    else
637        error.SetErrorString("invalid file handle");
638
639    num_bytes = 0;
640    data_buffer_sp.reset();
641    return error;
642}
643
644Error
645File::Write (const void *buf, size_t &num_bytes, off_t &offset)
646{
647    Error error;
648    int fd = GetDescriptor();
649    if (fd != kInvalidDescriptor)
650    {
651        ssize_t bytes_written = -1;
652        do
653        {
654            bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset);
655        } while (bytes_written < 0 && errno == EINTR);
656
657        if (bytes_written < 0)
658        {
659            num_bytes = 0;
660            error.SetErrorToErrno();
661        }
662        else
663        {
664            offset += bytes_written;
665            num_bytes = bytes_written;
666        }
667    }
668    else
669    {
670        num_bytes = 0;
671        error.SetErrorString("invalid file handle");
672    }
673    return error;
674}
675
676//------------------------------------------------------------------
677// Print some formatted output to the stream.
678//------------------------------------------------------------------
679size_t
680File::Printf (const char *format, ...)
681{
682    va_list args;
683    va_start (args, format);
684    size_t result = PrintfVarArg (format, args);
685    va_end (args);
686    return result;
687}
688
689//------------------------------------------------------------------
690// Print some formatted output to the stream.
691//------------------------------------------------------------------
692size_t
693File::PrintfVarArg (const char *format, va_list args)
694{
695    size_t result = 0;
696    if (DescriptorIsValid())
697    {
698        char *s = NULL;
699        result = vasprintf(&s, format, args);
700        if (s != NULL)
701        {
702            if (result > 0)
703            {
704                size_t s_len = result;
705                Write (s, s_len);
706                result = s_len;
707            }
708            free (s);
709        }
710    }
711    else if (StreamIsValid())
712    {
713        result = ::vfprintf (m_stream, format, args);
714    }
715    return result;
716}
717