1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "FileSystem.h"
31
32#include "FileMetadata.h"
33#include <dirent.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <fnmatch.h>
37#include <libgen.h>
38#include <stdio.h>
39#include <sys/stat.h>
40#include <sys/types.h>
41#include <unistd.h>
42#include <wtf/text/CString.h>
43#include <wtf/text/WTFString.h>
44
45namespace WebCore {
46
47bool fileExists(const String& path)
48{
49    if (path.isNull())
50        return false;
51
52    CString fsRep = fileSystemRepresentation(path);
53
54    if (!fsRep.data() || fsRep.data()[0] == '\0')
55        return false;
56
57    struct stat fileInfo;
58
59    // stat(...) returns 0 on successful stat'ing of the file, and non-zero in any case where the file doesn't exist or cannot be accessed
60    return !stat(fsRep.data(), &fileInfo);
61}
62
63bool deleteFile(const String& path)
64{
65    CString fsRep = fileSystemRepresentation(path);
66
67    if (!fsRep.data() || fsRep.data()[0] == '\0')
68        return false;
69
70    // unlink(...) returns 0 on successful deletion of the path and non-zero in any other case (including invalid permissions or non-existent file)
71    return !unlink(fsRep.data());
72}
73
74PlatformFileHandle openFile(const String& path, FileOpenMode mode)
75{
76    CString fsRep = fileSystemRepresentation(path);
77
78    if (fsRep.isNull())
79        return invalidPlatformFileHandle;
80
81    int platformFlag = 0;
82    if (mode == OpenForRead)
83        platformFlag |= O_RDONLY;
84    else if (mode == OpenForWrite)
85        platformFlag |= (O_WRONLY | O_CREAT | O_TRUNC);
86    return open(fsRep.data(), platformFlag, 0666);
87}
88
89void closeFile(PlatformFileHandle& handle)
90{
91    if (isHandleValid(handle)) {
92        close(handle);
93        handle = invalidPlatformFileHandle;
94    }
95}
96
97long long seekFile(PlatformFileHandle handle, long long offset, FileSeekOrigin origin)
98{
99    int whence = SEEK_SET;
100    switch (origin) {
101    case SeekFromBeginning:
102        whence = SEEK_SET;
103        break;
104    case SeekFromCurrent:
105        whence = SEEK_CUR;
106        break;
107    case SeekFromEnd:
108        whence = SEEK_END;
109        break;
110    default:
111        ASSERT_NOT_REACHED();
112    }
113    return static_cast<long long>(lseek(handle, offset, whence));
114}
115
116bool truncateFile(PlatformFileHandle handle, long long offset)
117{
118    // ftruncate returns 0 to indicate the success.
119    return !ftruncate(handle, offset);
120}
121
122int writeToFile(PlatformFileHandle handle, const char* data, int length)
123{
124    do {
125        int bytesWritten = write(handle, data, static_cast<size_t>(length));
126        if (bytesWritten >= 0)
127            return bytesWritten;
128    } while (errno == EINTR);
129    return -1;
130}
131
132int readFromFile(PlatformFileHandle handle, char* data, int length)
133{
134    do {
135        int bytesRead = read(handle, data, static_cast<size_t>(length));
136        if (bytesRead >= 0)
137            return bytesRead;
138    } while (errno == EINTR);
139    return -1;
140}
141
142#if USE(FILE_LOCK)
143bool lockFile(PlatformFileHandle handle, FileLockMode lockMode)
144{
145    COMPILE_ASSERT(LOCK_SH == LockShared, LockSharedEncodingIsAsExpected);
146    COMPILE_ASSERT(LOCK_EX == LockExclusive, LockExclusiveEncodingIsAsExpected);
147    COMPILE_ASSERT(LOCK_NB == LockNonBlocking, LockNonBlockingEncodingIsAsExpected);
148    int result = flock(handle, lockMode);
149    return (result != -1);
150}
151
152bool unlockFile(PlatformFileHandle handle)
153{
154    int result = flock(handle, LOCK_UN);
155    return (result != -1);
156}
157#endif
158
159bool deleteEmptyDirectory(const String& path)
160{
161    CString fsRep = fileSystemRepresentation(path);
162
163    if (!fsRep.data() || fsRep.data()[0] == '\0')
164        return false;
165
166    // rmdir(...) returns 0 on successful deletion of the path and non-zero in any other case (including invalid permissions or non-existent file)
167    return !rmdir(fsRep.data());
168}
169
170bool getFileSize(const String& path, long long& result)
171{
172    CString fsRep = fileSystemRepresentation(path);
173
174    if (!fsRep.data() || fsRep.data()[0] == '\0')
175        return false;
176
177    struct stat fileInfo;
178
179    if (stat(fsRep.data(), &fileInfo))
180        return false;
181
182    result = fileInfo.st_size;
183    return true;
184}
185
186bool getFileCreationTime(const String& path, time_t& result)
187{
188#if OS(DARWIN) || OS(OPENBSD) || OS(NETBSD) || OS(FREEBSD)
189    CString fsRep = fileSystemRepresentation(path);
190
191    if (!fsRep.data() || fsRep.data()[0] == '\0')
192        return false;
193
194    struct stat fileInfo;
195
196    if (stat(fsRep.data(), &fileInfo))
197        return false;
198
199    result = fileInfo.st_birthtime;
200    return true;
201#else
202    UNUSED_PARAM(path);
203    UNUSED_PARAM(result);
204    return false;
205#endif
206}
207
208bool getFileModificationTime(const String& path, time_t& result)
209{
210    CString fsRep = fileSystemRepresentation(path);
211
212    if (!fsRep.data() || fsRep.data()[0] == '\0')
213        return false;
214
215    struct stat fileInfo;
216
217    if (stat(fsRep.data(), &fileInfo))
218        return false;
219
220    result = fileInfo.st_mtime;
221    return true;
222}
223
224bool getFileMetadata(const String& path, FileMetadata& metadata)
225{
226    CString fsRep = fileSystemRepresentation(path);
227
228    if (!fsRep.data() || fsRep.data()[0] == '\0')
229        return false;
230
231    struct stat fileInfo;
232    if (stat(fsRep.data(), &fileInfo))
233        return false;
234
235    metadata.modificationTime = fileInfo.st_mtime;
236    metadata.length = fileInfo.st_size;
237    metadata.type = S_ISDIR(fileInfo.st_mode) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile;
238    return true;
239}
240
241String pathByAppendingComponent(const String& path, const String& component)
242{
243    if (path.endsWith('/'))
244        return path + component;
245    return path + "/" + component;
246}
247
248bool makeAllDirectories(const String& path)
249{
250    CString fullPath = fileSystemRepresentation(path);
251    if (!access(fullPath.data(), F_OK))
252        return true;
253
254    char* p = fullPath.mutableData() + 1;
255    int length = fullPath.length();
256
257    if(p[length - 1] == '/')
258        p[length - 1] = '\0';
259    for (; *p; ++p)
260        if (*p == '/') {
261            *p = '\0';
262            if (access(fullPath.data(), F_OK))
263                if (mkdir(fullPath.data(), S_IRWXU))
264                    return false;
265            *p = '/';
266        }
267    if (access(fullPath.data(), F_OK))
268        if (mkdir(fullPath.data(), S_IRWXU))
269            return false;
270
271    return true;
272}
273
274String pathGetFileName(const String& path)
275{
276    return path.substring(path.reverseFind('/') + 1);
277}
278
279String directoryName(const String& path)
280{
281    CString fsRep = fileSystemRepresentation(path);
282
283    if (!fsRep.data() || fsRep.data()[0] == '\0')
284        return String();
285
286    return dirname(fsRep.mutableData());
287}
288
289#if !PLATFORM(EFL)
290Vector<String> listDirectory(const String& path, const String& filter)
291{
292    Vector<String> entries;
293    CString cpath = path.utf8();
294    CString cfilter = filter.utf8();
295    DIR* dir = opendir(cpath.data());
296    if (dir) {
297        struct dirent* dp;
298        while ((dp = readdir(dir))) {
299            const char* name = dp->d_name;
300            if (!strcmp(name, ".") || !strcmp(name, ".."))
301                continue;
302            if (fnmatch(cfilter.data(), name, 0))
303                continue;
304            char filePath[1024];
305            if (static_cast<int>(sizeof(filePath) - 1) < snprintf(filePath, sizeof(filePath), "%s/%s", cpath.data(), name))
306                continue; // buffer overflow
307            entries.append(filePath);
308        }
309        closedir(dir);
310    }
311    return entries;
312}
313#endif
314
315#if !OS(DARWIN) || PLATFORM(EFL) || PLATFORM(GTK)
316String openTemporaryFile(const String& prefix, PlatformFileHandle& handle)
317{
318    char buffer[PATH_MAX];
319    const char* tmpDir = getenv("TMPDIR");
320
321    if (!tmpDir)
322        tmpDir = "/tmp";
323
324    if (snprintf(buffer, PATH_MAX, "%s/%sXXXXXX", tmpDir, prefix.utf8().data()) >= PATH_MAX)
325        goto end;
326
327    handle = mkstemp(buffer);
328    if (handle < 0)
329        goto end;
330
331    return String::fromUTF8(buffer);
332
333end:
334    handle = invalidPlatformFileHandle;
335    return String();
336}
337#endif
338
339} // namespace WebCore
340