94f24d5b097476e8c6911e747632a32388cb21de
[WebKit-https.git] / Source / WebCore / platform / glib / FileSystemGlib.cpp
1 /*
2  * Copyright (C) 2007, 2009 Holger Hans Peter Freyther
3  * Copyright (C) 2008 Collabora, Ltd.
4  * Copyright (C) 2008 Apple Inc. All rights reserved.
5  * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include "config.h"
24 #include "FileSystem.h"
25
26 #include "FileMetadata.h"
27 #include "NotImplemented.h"
28 #include <gio/gio.h>
29 #include <glib.h>
30 #include <glib/gstdio.h>
31 #include <wtf/UUID.h>
32 #include <wtf/glib/GLibUtilities.h>
33 #include <wtf/glib/GRefPtr.h>
34 #include <wtf/glib/GUniquePtr.h>
35 #include <wtf/text/CString.h>
36 #include <wtf/text/WTFString.h>
37
38 namespace WebCore {
39
40 /* On linux file names are just raw bytes, so also strings that cannot be encoded in any way
41  * are valid file names. This mean that we cannot just store a file name as-is in a String
42  * but we have to escape it.
43  * On Windows the GLib file name encoding is always UTF-8 so we can optimize this case. */
44 String stringFromFileSystemRepresentation(const char* fileSystemRepresentation)
45 {
46     if (!fileSystemRepresentation)
47         return String();
48
49 #if OS(WINDOWS)
50     return String::fromUTF8(fileSystemRepresentation);
51 #else
52     GUniquePtr<gchar> escapedString(g_uri_escape_string(fileSystemRepresentation, "/:", FALSE));
53     return escapedString.get();
54 #endif
55 }
56
57 static GUniquePtr<char> unescapedFilename(const String& path)
58 {
59     if (path.isEmpty())
60         return nullptr;
61 #if OS(WINDOWS)
62     return GUniquePtr<char>(g_strdup(path.utf8().data()));
63 #else
64     return GUniquePtr<char>(g_uri_unescape_string(path.utf8().data(), nullptr));
65 #endif
66 }
67
68 CString fileSystemRepresentation(const String& path)
69 {
70 #if OS(WINDOWS)
71     return path.utf8();
72 #else
73     GUniquePtr<gchar> filename = unescapedFilename(path);
74     return filename.get();
75 #endif
76 }
77
78 // Converts a string to something suitable to be displayed to the user.
79 String filenameForDisplay(const String& string)
80 {
81 #if OS(WINDOWS)
82     return string;
83 #else
84     GUniquePtr<gchar> filename = unescapedFilename(string);
85     if (!filename)
86         return string;
87
88     GUniquePtr<gchar> display(g_filename_to_utf8(filename.get(), -1, nullptr, nullptr, nullptr));
89     if (!display)
90         return string;
91
92     return String::fromUTF8(display.get());
93 #endif
94 }
95
96 bool fileExists(const String& path)
97 {
98     GUniquePtr<gchar> filename = unescapedFilename(path);
99     return filename ? g_file_test(filename.get(), G_FILE_TEST_EXISTS) : false;
100 }
101
102 bool deleteFile(const String& path)
103 {
104     GUniquePtr<gchar> filename = unescapedFilename(path);
105     return filename ? g_remove(filename.get()) != -1 : false;
106 }
107
108 bool deleteEmptyDirectory(const String& path)
109 {
110     GUniquePtr<gchar> filename = unescapedFilename(path);
111     return filename ? g_rmdir(filename.get()) != -1 : false;
112 }
113
114 static bool getFileStat(const String& path, GStatBuf* statBuffer)
115 {
116     GUniquePtr<gchar> filename = unescapedFilename(path);
117     if (!filename)
118         return false;
119
120     return g_stat(filename.get(), statBuffer) != -1;
121 }
122
123 bool getFileSize(const String& path, long long& resultSize)
124 {
125     GStatBuf statResult;
126     if (!getFileStat(path, &statResult))
127         return false;
128
129     resultSize = statResult.st_size;
130     return true;
131 }
132
133 bool getFileSize(PlatformFileHandle, long long&)
134 {
135     notImplemented();
136     return false;
137 }
138
139 bool getFileCreationTime(const String&, time_t&)
140 {
141     // FIXME: Is there a way to retrieve file creation time with Gtk on platforms that support it?
142     return false;
143 }
144
145 bool getFileModificationTime(const String& path, time_t& modifiedTime)
146 {
147     GStatBuf statResult;
148     if (!getFileStat(path, &statResult))
149         return false;
150
151     modifiedTime = statResult.st_mtime;
152     return true;
153 }
154
155 bool getFileMetadata(const String& path, FileMetadata& metadata)
156 {
157     GStatBuf statResult;
158     if (!getFileStat(path, &statResult))
159         return false;
160
161     metadata.modificationTime = statResult.st_mtime;
162     metadata.length = statResult.st_size;
163     metadata.type = S_ISDIR(statResult.st_mode) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile;
164     return true;
165 }
166
167 String pathByAppendingComponent(const String& path, const String& component)
168 {
169     if (path.endsWith(G_DIR_SEPARATOR_S))
170         return path + component;
171     return path + G_DIR_SEPARATOR_S + component;
172 }
173
174 bool makeAllDirectories(const String& path)
175 {
176     GUniquePtr<gchar> filename = unescapedFilename(path);
177     return filename ? g_mkdir_with_parents(filename.get(), S_IRWXU) != -1 : false;
178 }
179
180 String homeDirectoryPath()
181 {
182     return stringFromFileSystemRepresentation(g_get_home_dir());
183 }
184
185 String pathGetFileName(const String& pathName)
186 {
187     GUniquePtr<gchar> tmpFilename = unescapedFilename(pathName);
188     if (!tmpFilename)
189         return pathName;
190
191     GUniquePtr<gchar> baseName(g_path_get_basename(tmpFilename.get()));
192     return String::fromUTF8(baseName.get());
193 }
194
195 CString applicationDirectoryPath()
196 {
197     CString path = getCurrentExecutablePath();
198     if (!path.isNull())
199         return path;
200
201     // If the above fails, check the PATH env variable.
202     GUniquePtr<char> currentExePath(g_find_program_in_path(g_get_prgname()));
203     if (!currentExePath.get())
204         return CString();
205
206     GUniquePtr<char> dirname(g_path_get_dirname(currentExePath.get()));
207     return dirname.get();
208 }
209
210 CString sharedResourcesPath()
211 {
212     static CString cachedPath;
213     if (!cachedPath.isNull())
214         return cachedPath;
215
216 #if PLATFORM(WPE)
217     GUniquePtr<gchar> dataPath(g_build_filename(DATA_DIR, "wpe", nullptr));
218 #elif PLATFORM(GTK)
219 #if OS(WINDOWS)
220     HMODULE hmodule = 0;
221     GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(sharedResourcesPath), &hmodule);
222
223     GUniquePtr<gchar> runtimeDir(g_win32_get_package_installation_directory_of_module(hmodule));
224     GUniquePtr<gchar> dataPath(g_build_filename(runtimeDir.get(), "share", "webkitgtk-" WEBKITGTK_API_VERSION_STRING, NULL));
225 #else
226     GUniquePtr<gchar> dataPath(g_build_filename(DATA_DIR, "webkitgtk-" WEBKITGTK_API_VERSION_STRING, NULL));
227 #endif
228 #endif
229
230     cachedPath = dataPath.get();
231     return cachedPath;
232 }
233
234 bool getVolumeFreeSpace(const String& path, uint64_t& freeSpace)
235 {
236     GUniquePtr<gchar> filename = unescapedFilename(path);
237     if (!filename)
238         return false;
239
240     GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(filename.get()));
241     GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_filesystem_info(file.get(), G_FILE_ATTRIBUTE_FILESYSTEM_FREE, 0, 0));
242     if (!fileInfo)
243         return false;
244
245     freeSpace = g_file_info_get_attribute_uint64(fileInfo.get(), G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
246     return !!freeSpace;
247 }
248
249 String directoryName(const String& path)
250 {
251     GUniquePtr<gchar> filename = unescapedFilename(path);
252     if (!filename)
253         return String();
254
255     GUniquePtr<char> dirname(g_path_get_dirname(filename.get()));
256     return String::fromUTF8(dirname.get());
257 }
258
259 Vector<String> listDirectory(const String& path, const String& filter)
260 {
261     Vector<String> entries;
262
263     GUniquePtr<gchar> filename = unescapedFilename(path);
264     if (!filename)
265         return entries;
266
267     GUniquePtr<GDir> dir(g_dir_open(filename.get(), 0, nullptr));
268     if (!dir)
269         return entries;
270
271     GUniquePtr<GPatternSpec> pspec(g_pattern_spec_new((filter.utf8()).data()));
272     while (const char* name = g_dir_read_name(dir.get())) {
273         if (!g_pattern_match_string(pspec.get(), name))
274             continue;
275
276         GUniquePtr<gchar> entry(g_build_filename(filename.get(), name, nullptr));
277         entries.append(stringFromFileSystemRepresentation(entry.get()));
278     }
279
280     return entries;
281 }
282
283 String openTemporaryFile(const String& prefix, PlatformFileHandle& handle)
284 {
285     GUniquePtr<gchar> filename(g_strdup_printf("%s%s", prefix.utf8().data(), createCanonicalUUIDString().utf8().data()));
286     GUniquePtr<gchar> tempPath(g_build_filename(g_get_tmp_dir(), filename.get(), NULL));
287     GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(tempPath.get()));
288
289     handle = g_file_create_readwrite(file.get(), G_FILE_CREATE_NONE, 0, 0);
290     if (!isHandleValid(handle))
291         return String();
292     return String::fromUTF8(tempPath.get());
293 }
294
295 PlatformFileHandle openFile(const String& path, FileOpenMode mode)
296 {
297     GUniquePtr<gchar> filename = unescapedFilename(path);
298     if (!filename)
299         return invalidPlatformFileHandle;
300
301     GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(filename.get()));
302     GFileIOStream* ioStream = 0;
303     if (mode == OpenForRead)
304         ioStream = g_file_open_readwrite(file.get(), 0, 0);
305     else if (mode == OpenForWrite) {
306         if (g_file_test(filename.get(), static_cast<GFileTest>(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)))
307             ioStream = g_file_open_readwrite(file.get(), 0, 0);
308         else
309             ioStream = g_file_create_readwrite(file.get(), G_FILE_CREATE_NONE, 0, 0);
310     }
311
312     return ioStream;
313 }
314
315 void closeFile(PlatformFileHandle& handle)
316 {
317     if (!isHandleValid(handle))
318         return;
319
320     g_io_stream_close(G_IO_STREAM(handle), 0, 0);
321     g_object_unref(handle);
322     handle = invalidPlatformFileHandle;
323 }
324
325 long long seekFile(PlatformFileHandle handle, long long offset, FileSeekOrigin origin)
326 {
327     GSeekType seekType = G_SEEK_SET;
328     switch (origin) {
329     case SeekFromBeginning:
330         seekType = G_SEEK_SET;
331         break;
332     case SeekFromCurrent:
333         seekType = G_SEEK_CUR;
334         break;
335     case SeekFromEnd:
336         seekType = G_SEEK_END;
337         break;
338     default:
339         ASSERT_NOT_REACHED();
340     }
341
342     if (!g_seekable_seek(G_SEEKABLE(g_io_stream_get_input_stream(G_IO_STREAM(handle))),
343         offset, seekType, 0, 0))
344     {
345         return -1;
346     }
347     return g_seekable_tell(G_SEEKABLE(g_io_stream_get_input_stream(G_IO_STREAM(handle))));
348 }
349
350 int writeToFile(PlatformFileHandle handle, const char* data, int length)
351 {
352     gsize bytesWritten;
353     g_output_stream_write_all(g_io_stream_get_output_stream(G_IO_STREAM(handle)),
354         data, length, &bytesWritten, 0, 0);
355     return bytesWritten;
356 }
357
358 int readFromFile(PlatformFileHandle handle, char* data, int length)
359 {
360     GUniqueOutPtr<GError> error;
361     do {
362         gssize bytesRead = g_input_stream_read(g_io_stream_get_input_stream(G_IO_STREAM(handle)),
363             data, length, 0, &error.outPtr());
364         if (bytesRead >= 0)
365             return bytesRead;
366     } while (error && error->code == G_FILE_ERROR_INTR);
367     return -1;
368 }
369
370 bool moveFile(const String& oldPath, const String& newPath)
371 {
372     GUniquePtr<gchar> oldFilename = unescapedFilename(oldPath);
373     if (!oldFilename)
374         return false;
375
376     GUniquePtr<gchar> newFilename = unescapedFilename(newPath);
377     if (!newFilename)
378         return false;
379
380     return g_rename(oldFilename.get(), newFilename.get()) != -1;
381 }
382
383 bool hardLinkOrCopyFile(const String& source, const String& destination)
384 {
385 #if OS(WINDOWS)
386     return !!::CopyFile(source.charactersWithNullTermination().data(), destination.charactersWithNullTermination().data(), TRUE);
387 #else
388     GUniquePtr<gchar> sourceFilename = unescapedFilename(source);
389     if (!sourceFilename)
390         return false;
391
392     GUniquePtr<gchar> destinationFilename = unescapedFilename(destination);
393     if (!destinationFilename)
394         return false;
395
396     if (!link(sourceFilename.get(), destinationFilename.get()))
397         return true;
398
399     // Hard link failed. Perform a copy instead.
400     GRefPtr<GFile> sourceFile = adoptGRef(g_file_new_for_path(sourceFilename.get()));
401     GRefPtr<GFile> destinationFile = adoptGRef(g_file_new_for_path(destinationFilename.get()));
402     return g_file_copy(sourceFile.get(), destinationFile.get(), G_FILE_COPY_NONE, nullptr, nullptr, nullptr, nullptr);
403 #endif
404 }
405
406 std::optional<int32_t> getFileDeviceId(const CString& fsFile)
407 {
408     GUniquePtr<gchar> filename = unescapedFilename(fsFile.data());
409     if (!filename)
410         return std::nullopt;
411
412     GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(filename.get()));
413     GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_filesystem_info(file.get(), G_FILE_ATTRIBUTE_UNIX_DEVICE, nullptr, nullptr));
414     if (!fileInfo)
415         return std::nullopt;
416
417     return g_file_info_get_attribute_uint32(fileInfo.get(), G_FILE_ATTRIBUTE_UNIX_DEVICE);
418 }
419
420 }