1// Copyright (c) 2013 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// A tool to dump HTML5 filesystem from CUI.
6//
7// Usage:
8//
9// ./out/Release/dump_file_system [options] <filesystem dir> [origin]...
10//
11// If no origin is specified, this dumps all origins in the profile dir.
12//
13// Available options:
14//
15// -t : dumps temporary files instead of persistent.
16// -s : dumps syncable files instead of persistent.
17// -l : more information will be displayed.
18//
19// The format of -l option is:
20//
21// === ORIGIN origin_name origin_dir ===
22// file_name file_id file_size file_content_path
23// ...
24//
25// where file_name has a trailing slash, file_size is the number of
26// children, and file_content_path is empty if the file is a directory.
27//
28
29#include <stdio.h>
30#include <stdlib.h>
31
32#include <stack>
33#include <string>
34#include <utility>
35#include <vector>
36
37#include "base/files/file_path.h"
38#include "base/files/file_util.h"
39#include "base/format_macros.h"
40#include "base/strings/stringprintf.h"
41#include "storage/browser/fileapi/obfuscated_file_util.h"
42#include "storage/browser/fileapi/sandbox_directory_database.h"
43#include "storage/browser/fileapi/sandbox_file_system_backend.h"
44#include "storage/browser/fileapi/sandbox_origin_database.h"
45#include "storage/common/fileapi/file_system_types.h"
46#include "storage/common/fileapi/file_system_util.h"
47
48namespace {
49
50bool g_opt_long;
51const char* g_opt_fs_type = "p";
52
53void ShowMessageAndExit(const std::string& msg) {
54  fprintf(stderr, "%s\n", msg.c_str());
55  exit(EXIT_FAILURE);
56}
57
58void ShowUsageAndExit(const std::string& arg0) {
59  ShowMessageAndExit(
60      "Usage: " + arg0 +
61      " [-l] [-t] [-s] <filesystem dir> [origin]...");
62}
63
64}  // namespace
65
66namespace storage {
67
68static void DumpDirectoryTree(const std::string& origin_name,
69                              base::FilePath origin_dir) {
70  origin_dir = origin_dir.Append(g_opt_fs_type);
71
72  printf("=== ORIGIN %s %s ===\n",
73         origin_name.c_str(), FilePathToString(origin_dir).c_str());
74
75  if (!base::DirectoryExists(origin_dir))
76    return;
77
78  SandboxDirectoryDatabase directory_db(origin_dir, NULL);
79  SandboxDirectoryDatabase::FileId root_id;
80  if (!directory_db.GetFileWithPath(StringToFilePath("/"), &root_id))
81    return;
82
83  std::stack<std::pair<SandboxDirectoryDatabase::FileId,
84                       std::string> > paths;
85  paths.push(std::make_pair(root_id, ""));
86  while (!paths.empty()) {
87    SandboxDirectoryDatabase::FileId id = paths.top().first;
88    const std::string dirname = paths.top().second;
89    paths.pop();
90
91    SandboxDirectoryDatabase::FileInfo info;
92    if (!directory_db.GetFileInfo(id, &info)) {
93      ShowMessageAndExit(base::StringPrintf("GetFileInfo failed for %"PRId64,
94                                            id));
95    }
96
97    const std::string name =
98        dirname + "/" + FilePathToString(base::FilePath(info.name));
99    std::vector<SandboxDirectoryDatabase::FileId> children;
100    if (info.is_directory()) {
101      if (!directory_db.ListChildren(id, &children)) {
102        ShowMessageAndExit(base::StringPrintf(
103            "ListChildren failed for %s (%"PRId64")",
104            info.name.c_str(), id));
105      }
106
107      for (size_t j = children.size(); j; j--)
108        paths.push(make_pair(children[j-1], name));
109    }
110
111    // +1 for the leading extra slash.
112    const char* display_name = name.c_str() + 1;
113    const char* directory_suffix = info.is_directory() ? "/" : "";
114    if (g_opt_long) {
115      int64 size;
116      if (info.is_directory()) {
117        size = static_cast<int64>(children.size());
118      } else {
119        base::GetFileSize(origin_dir.Append(info.data_path), &size);
120      }
121      // TODO(hamaji): Modification time?
122      printf("%s%s %"PRId64" %"PRId64" %s\n",
123             display_name,
124             directory_suffix,
125             id,
126             size,
127             FilePathToString(info.data_path).c_str());
128    } else {
129      printf("%s%s\n", display_name, directory_suffix);
130    }
131  }
132}
133
134static void DumpOrigin(const base::FilePath& file_system_dir,
135                       const std::string& origin_name) {
136  SandboxOriginDatabase origin_db(file_system_dir, NULL);
137  base::FilePath origin_dir;
138  if (!origin_db.HasOriginPath(origin_name)) {
139    ShowMessageAndExit("Origin " + origin_name + " is not in " +
140                       FilePathToString(file_system_dir));
141  }
142
143  if (!origin_db.GetPathForOrigin(origin_name, &origin_dir)) {
144    ShowMessageAndExit("Failed to get path of origin " + origin_name +
145                       " in " + FilePathToString(file_system_dir));
146  }
147  DumpDirectoryTree(origin_name, file_system_dir.Append(origin_dir));
148}
149
150static void DumpFileSystem(const base::FilePath& file_system_dir) {
151  SandboxOriginDatabase origin_db(file_system_dir, NULL);
152  std::vector<SandboxOriginDatabase::OriginRecord> origins;
153  origin_db.ListAllOrigins(&origins);
154  for (size_t i = 0; i < origins.size(); i++) {
155    const SandboxOriginDatabase::OriginRecord& origin = origins[i];
156    DumpDirectoryTree(origin.origin, file_system_dir.Append(origin.path));
157    puts("");
158  }
159}
160
161}  // namespace storage
162
163int main(int argc, char* argv[]) {
164  const char* arg0 = argv[0];
165  std::string username = "Default";
166  while (true) {
167    if (argc < 2)
168      ShowUsageAndExit(arg0);
169
170    if (std::string(argv[1]) == "-l") {
171      g_opt_long = true;
172      argc--;
173      argv++;
174    } else if (std::string(argv[1]) == "-t") {
175      g_opt_fs_type = "t";
176      argc--;
177      argv++;
178    } else if (std::string(argv[1]) == "-s") {
179      g_opt_fs_type = "s";
180      argc--;
181      argv++;
182    } else {
183      break;
184    }
185  }
186
187  if (argc < 2)
188    ShowUsageAndExit(arg0);
189
190  const base::FilePath file_system_dir = storage::StringToFilePath(argv[1]);
191  if (!base::DirectoryExists(file_system_dir)) {
192    ShowMessageAndExit(storage::FilePathToString(file_system_dir) +
193                       " is not a filesystem directory");
194  }
195
196  if (argc == 2) {
197    storage::DumpFileSystem(file_system_dir);
198  } else {
199    for (int i = 2; i < argc; i++) {
200      storage::DumpOrigin(file_system_dir, argv[i]);
201    }
202  }
203  return 0;
204}
205