1 //===-- FileSystem.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 #include "lldb/Host/FileSystem.h"
11 
12 // C includes
13 #include <dirent.h>
14 #include <sys/mount.h>
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #ifdef __linux__
19 #include <sys/statfs.h>
20 #include <sys/mount.h>
21 #include <linux/magic.h>
22 #endif
23 
24 // lldb Includes
25 #include "lldb/Core/Error.h"
26 #include "lldb/Core/StreamString.h"
27 #include "lldb/Host/Host.h"
28 
29 using namespace lldb;
30 using namespace lldb_private;
31 
32 FileSpec::PathSyntax
33 FileSystem::GetNativePathSyntax()
34 {
35     return FileSpec::ePathSyntaxPosix;
36 }
37 
38 Error
39 FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
40 {
41     if (file_spec)
42     {
43         Error error;
44         if (::mkdir(file_spec.GetCString(), file_permissions) == -1)
45         {
46             error.SetErrorToErrno();
47             errno = 0;
48             switch (error.GetError())
49             {
50                 case ENOENT:
51                 {
52                     // Parent directory doesn't exist, so lets make it if we can
53                     // Make the parent directory and try again
54                     FileSpec parent_file_spec{file_spec.GetDirectory().GetCString(), false};
55                     error = MakeDirectory(parent_file_spec, file_permissions);
56                     if (error.Fail())
57                         return error;
58                     // Try and make the directory again now that the parent directory was made successfully
59                     if (::mkdir(file_spec.GetCString(), file_permissions) == -1)
60                     {
61                         error.SetErrorToErrno();
62                         return error;
63                     }
64                 }
65                 case EEXIST:
66                 {
67                     if (file_spec.IsDirectory())
68                         return Error{}; // It is a directory and it already exists
69                 }
70             }
71         }
72         return error;
73     }
74     return Error{"empty path"};
75 }
76 
77 Error
78 FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
79 {
80     Error error;
81     if (file_spec)
82     {
83         if (recurse)
84         {
85             // Save all sub directories in a list so we don't recursively call this function
86             // and possibly run out of file descriptors if the directory is too deep.
87             std::vector<FileSpec> sub_directories;
88 
89             FileSpec::ForEachItemInDirectory (file_spec.GetCString(), [&error, &sub_directories](FileSpec::FileType file_type, const FileSpec &spec) -> FileSpec::EnumerateDirectoryResult {
90                 if (file_type == FileSpec::eFileTypeDirectory)
91                 {
92                     // Save all directorires and process them after iterating through this directory
93                     sub_directories.push_back(spec);
94                 }
95                 else
96                 {
97                     // Update sub_spec to point to the current file and delete it
98                     error = FileSystem::Unlink(spec);
99                 }
100                 // If anything went wrong, stop iterating, else process the next file
101                 if (error.Fail())
102                     return FileSpec::eEnumerateDirectoryResultQuit;
103                 else
104                     return FileSpec::eEnumerateDirectoryResultNext;
105             });
106 
107             if (error.Success())
108             {
109                 // Now delete all sub directories with separate calls that aren't
110                 // recursively calling into this function _while_ this function is
111                 // iterating through the current directory.
112                 for (const auto &sub_directory : sub_directories)
113                 {
114                     error = DeleteDirectory(sub_directory, recurse);
115                     if (error.Fail())
116                         break;
117                 }
118             }
119         }
120 
121         if (error.Success())
122         {
123             if (::rmdir(file_spec.GetCString()) != 0)
124                 error.SetErrorToErrno();
125         }
126     }
127     else
128     {
129         error.SetErrorString("empty path");
130     }
131     return error;
132 }
133 
134 Error
135 FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
136 {
137     Error error;
138     struct stat file_stats;
139     if (::stat(file_spec.GetCString(), &file_stats) == 0)
140     {
141         // The bits in "st_mode" currently match the definitions
142         // for the file mode bits in unix.
143         file_permissions = file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
144     }
145     else
146     {
147         error.SetErrorToErrno();
148     }
149     return error;
150 }
151 
152 Error
153 FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
154 {
155     Error error;
156     if (::chmod(file_spec.GetCString(), file_permissions) != 0)
157         error.SetErrorToErrno();
158     return error;
159 }
160 
161 lldb::user_id_t
162 FileSystem::GetFileSize(const FileSpec &file_spec)
163 {
164     return file_spec.GetByteSize();
165 }
166 
167 bool
168 FileSystem::GetFileExists(const FileSpec &file_spec)
169 {
170     return file_spec.Exists();
171 }
172 
173 Error
174 FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
175 {
176     Error error;
177     if (::link(dst.GetCString(), src.GetCString()) == -1)
178         error.SetErrorToErrno();
179     return error;
180 }
181 
182 Error
183 FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
184 {
185     Error error;
186     if (::symlink(dst.GetCString(), src.GetCString()) == -1)
187         error.SetErrorToErrno();
188     return error;
189 }
190 
191 Error
192 FileSystem::Unlink(const FileSpec &file_spec)
193 {
194     Error error;
195     if (::unlink(file_spec.GetCString()) == -1)
196         error.SetErrorToErrno();
197     return error;
198 }
199 
200 Error
201 FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
202 {
203     Error error;
204     char buf[PATH_MAX];
205     ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1);
206     if (count < 0)
207         error.SetErrorToErrno();
208     else
209     {
210         buf[count] = '\0'; // Success
211         dst.SetFile(buf, false);
212     }
213     return error;
214 }
215 
216 static bool IsLocal(const struct statfs& info)
217 {
218 #ifdef __linux__
219     #define CIFS_MAGIC_NUMBER 0xFF534D42
220     switch ((uint32_t)info.f_type)
221     {
222     case NFS_SUPER_MAGIC:
223     case SMB_SUPER_MAGIC:
224     case CIFS_MAGIC_NUMBER:
225         return false;
226     default:
227         return true;
228     }
229 #else
230     return (info.f_flags & MNT_LOCAL) != 0;
231 #endif
232 }
233 
234 bool
235 FileSystem::IsLocal(const FileSpec &spec)
236 {
237     struct statfs statfs_info;
238     std::string path (spec.GetPath());
239     if (statfs(path.c_str(), &statfs_info) == 0)
240         return ::IsLocal(statfs_info);
241     return false;
242 }
243