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 Computer, 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 getFileModificationTime(const String& path, time_t& result)
187{
188    CString fsRep = fileSystemRepresentation(path);
189
190    if (!fsRep.data() || fsRep.data()[0] == '\0')
191        return false;
192
193    struct stat fileInfo;
194
195    if (stat(fsRep.data(), &fileInfo))
196        return false;
197
198    result = fileInfo.st_mtime;
199    return true;
200}
201
202bool getFileMetadata(const String& path, FileMetadata& metadata)
203{
204    CString fsRep = fileSystemRepresentation(path);
205
206    if (!fsRep.data() || fsRep.data()[0] == '\0')
207        return false;
208
209    struct stat fileInfo;
210    if (stat(fsRep.data(), &fileInfo))
211        return false;
212
213    metadata.modificationTime = fileInfo.st_mtime;
214    metadata.length = fileInfo.st_size;
215    metadata.type = S_ISDIR(fileInfo.st_mode) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile;
216    return true;
217}
218
219String pathByAppendingComponent(const String& path, const String& component)
220{
221    if (path.endsWith('/'))
222        return path + component;
223    return path + "/" + component;
224}
225
226bool makeAllDirectories(const String& path)
227{
228    CString fullPath = fileSystemRepresentation(path);
229    if (!access(fullPath.data(), F_OK))
230        return true;
231
232    char* p = fullPath.mutableData() + 1;
233    int length = fullPath.length();
234
235    if(p[length - 1] == '/')
236        p[length - 1] = '\0';
237    for (; *p; ++p)
238        if (*p == '/') {
239            *p = '\0';
240            if (access(fullPath.data(), F_OK))
241                if (mkdir(fullPath.data(), S_IRWXU))
242                    return false;
243            *p = '/';
244        }
245    if (access(fullPath.data(), F_OK))
246        if (mkdir(fullPath.data(), S_IRWXU))
247            return false;
248
249    return true;
250}
251
252String pathGetFileName(const String& path)
253{
254    return path.substring(path.reverseFind('/') + 1);
255}
256
257String directoryName(const String& path)
258{
259    CString fsRep = fileSystemRepresentation(path);
260
261    if (!fsRep.data() || fsRep.data()[0] == '\0')
262        return String();
263
264    return dirname(fsRep.mutableData());
265}
266
267#if !PLATFORM(EFL)
268Vector<String> listDirectory(const String& path, const String& filter)
269{
270    Vector<String> entries;
271    CString cpath = path.utf8();
272    CString cfilter = filter.utf8();
273    DIR* dir = opendir(cpath.data());
274    if (dir) {
275        struct dirent* dp;
276        while ((dp = readdir(dir))) {
277            const char* name = dp->d_name;
278            if (!strcmp(name, ".") || !strcmp(name, ".."))
279                continue;
280            if (fnmatch(cfilter.data(), name, 0))
281                continue;
282            char filePath[1024];
283            if (static_cast<int>(sizeof(filePath) - 1) < snprintf(filePath, sizeof(filePath), "%s/%s", cpath.data(), name))
284                continue; // buffer overflow
285            entries.append(filePath);
286        }
287        closedir(dir);
288    }
289    return entries;
290}
291#endif
292
293#if !PLATFORM(MAC)
294String openTemporaryFile(const String& prefix, PlatformFileHandle& handle)
295{
296    char buffer[PATH_MAX];
297    const char* tmpDir = getenv("TMPDIR");
298
299    if (!tmpDir)
300        tmpDir = "/tmp";
301
302    if (snprintf(buffer, PATH_MAX, "%s/%sXXXXXX", tmpDir, prefix.utf8().data()) >= PATH_MAX)
303        goto end;
304
305    handle = mkstemp(buffer);
306    if (handle < 0)
307        goto end;
308
309    return String::fromUTF8(buffer);
310
311end:
312    handle = invalidPlatformFileHandle;
313    return String();
314}
315#endif
316
317} // namespace WebCore
318