1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "storage/browser/database/vfs_backend.h"
6
7#include "base/files/file_path.h"
8#include "base/files/file_util.h"
9#include "base/logging.h"
10#include "third_party/sqlite/sqlite3.h"
11
12namespace storage {
13
14static const int kFileTypeMask = 0x00007F00;
15
16// static
17bool VfsBackend::OpenTypeIsReadWrite(int desired_flags) {
18  return (desired_flags & SQLITE_OPEN_READWRITE) != 0;
19}
20
21// static
22bool VfsBackend::OpenFileFlagsAreConsistent(int desired_flags) {
23  const int file_type = desired_flags & kFileTypeMask;
24  const bool is_exclusive = (desired_flags & SQLITE_OPEN_EXCLUSIVE) != 0;
25  const bool is_delete = (desired_flags & SQLITE_OPEN_DELETEONCLOSE) != 0;
26  const bool is_create = (desired_flags & SQLITE_OPEN_CREATE) != 0;
27  const bool is_read_only = (desired_flags & SQLITE_OPEN_READONLY) != 0;
28  const bool is_read_write = (desired_flags & SQLITE_OPEN_READWRITE) != 0;
29
30  // All files should be opened either read-write or read-only, but not both.
31  if (is_read_only == is_read_write)
32    return false;
33
34  // If a new file is created, it must also be writable.
35  if (is_create && !is_read_write)
36    return false;
37
38  // If we're accessing an existing file, we cannot give exclusive access, and
39  // we can't delete it.
40  // Normally, we'd also check that 'is_delete' is false for a main DB, main
41  // journal or master journal file; however, when in incognito mode, we use
42  // the SQLITE_OPEN_DELETEONCLOSE flag when opening those files too and keep
43  // an open handle to them for as long as the incognito profile is around.
44  if ((is_exclusive || is_delete) && !is_create)
45    return false;
46
47  // Make sure we're opening the DB directory or that a file type is set.
48  return (file_type == SQLITE_OPEN_MAIN_DB) ||
49         (file_type == SQLITE_OPEN_TEMP_DB) ||
50         (file_type == SQLITE_OPEN_MAIN_JOURNAL) ||
51         (file_type == SQLITE_OPEN_TEMP_JOURNAL) ||
52         (file_type == SQLITE_OPEN_SUBJOURNAL) ||
53         (file_type == SQLITE_OPEN_MASTER_JOURNAL) ||
54         (file_type == SQLITE_OPEN_TRANSIENT_DB);
55}
56
57// static
58base::File VfsBackend::OpenFile(const base::FilePath& file_path,
59                                int desired_flags) {
60  DCHECK(!file_path.empty());
61
62  // Verify the flags for consistency and create the database
63  // directory if it doesn't exist.
64  if (!OpenFileFlagsAreConsistent(desired_flags) ||
65      !base::CreateDirectory(file_path.DirName())) {
66    return base::File();
67  }
68
69  int flags = 0;
70  flags |= base::File::FLAG_READ;
71  if (desired_flags & SQLITE_OPEN_READWRITE)
72    flags |= base::File::FLAG_WRITE;
73
74  if (!(desired_flags & SQLITE_OPEN_MAIN_DB))
75    flags |= base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_EXCLUSIVE_WRITE;
76
77  flags |= ((desired_flags & SQLITE_OPEN_CREATE) ?
78           base::File::FLAG_OPEN_ALWAYS : base::File::FLAG_OPEN);
79
80  if (desired_flags & SQLITE_OPEN_EXCLUSIVE)
81    flags |= base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_EXCLUSIVE_WRITE;
82
83  if (desired_flags & SQLITE_OPEN_DELETEONCLOSE) {
84    flags |= base::File::FLAG_TEMPORARY | base::File::FLAG_HIDDEN |
85             base::File::FLAG_DELETE_ON_CLOSE;
86  }
87
88  // This flag will allow us to delete the file later on from the browser
89  // process.
90  flags |= base::File::FLAG_SHARE_DELETE;
91
92  // Try to open/create the DB file.
93  return base::File(file_path, flags);
94}
95
96// static
97base::File VfsBackend::OpenTempFileInDirectory(const base::FilePath& dir_path,
98                                               int desired_flags) {
99  // We should be able to delete temp files when they're closed
100  // and create them as needed
101  if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE) ||
102      !(desired_flags & SQLITE_OPEN_CREATE)) {
103    return base::File();
104  }
105
106  // Get a unique temp file name in the database directory.
107  base::FilePath temp_file_path;
108  if (!base::CreateTemporaryFileInDir(dir_path, &temp_file_path))
109    return base::File();
110
111  return OpenFile(temp_file_path, desired_flags);
112}
113
114// static
115int VfsBackend::DeleteFile(const base::FilePath& file_path, bool sync_dir) {
116  if (!base::PathExists(file_path))
117    return SQLITE_OK;
118  if (!base::DeleteFile(file_path, false))
119    return SQLITE_IOERR_DELETE;
120
121  int error_code = SQLITE_OK;
122#if defined(OS_POSIX)
123  if (sync_dir) {
124    base::File dir(file_path.DirName(), base::File::FLAG_READ);
125    if (dir.IsValid()) {
126      if (!dir.Flush())
127        error_code = SQLITE_IOERR_DIR_FSYNC;
128    } else {
129      error_code = SQLITE_CANTOPEN;
130    }
131  }
132#endif
133  return error_code;
134}
135
136// static
137uint32 VfsBackend::GetFileAttributes(const base::FilePath& file_path) {
138#if defined(OS_WIN)
139  uint32 attributes = ::GetFileAttributes(file_path.value().c_str());
140#elif defined(OS_POSIX)
141  uint32 attributes = 0;
142  if (!access(file_path.value().c_str(), R_OK))
143    attributes |= static_cast<uint32>(R_OK);
144  if (!access(file_path.value().c_str(), W_OK))
145    attributes |= static_cast<uint32>(W_OK);
146  if (!attributes)
147    attributes = -1;
148#endif
149  return attributes;
150}
151
152// static
153int64 VfsBackend::GetFileSize(const base::FilePath& file_path) {
154  int64 size = 0;
155  return (base::GetFileSize(file_path, &size) ? size : 0);
156}
157
158}  // namespace storage
159