isolated_context.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // Copyright (c) 2012 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. #include "storage/browser/file_system/isolated_context.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <memory>
  8. #include "base/check_op.h"
  9. #include "base/notreached.h"
  10. #include "base/rand_util.h"
  11. #include "base/strings/string_number_conversions.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/strings/stringprintf.h"
  14. #include "storage/browser/file_system/file_system_url.h"
  15. #include "third_party/blink/public/common/storage_key/storage_key.h"
  16. class GURL;
  17. namespace storage {
  18. namespace {
  19. base::FilePath::StringType GetRegisterNameForPath(const base::FilePath& path) {
  20. // If it's not a root path simply return a base name.
  21. if (path.DirName() != path)
  22. return path.BaseName().value();
  23. #if defined(FILE_PATH_USES_DRIVE_LETTERS)
  24. base::FilePath::StringType name;
  25. for (size_t i = 0;
  26. i < path.value().size() && !base::FilePath::IsSeparator(path.value()[i]);
  27. ++i) {
  28. if (path.value()[i] == L':') {
  29. name.append(L"_drive");
  30. break;
  31. }
  32. name.append(1, path.value()[i]);
  33. }
  34. return name;
  35. #else
  36. return FILE_PATH_LITERAL("<root>");
  37. #endif
  38. }
  39. bool IsSinglePathIsolatedFileSystem(FileSystemType type) {
  40. DCHECK_NE(kFileSystemTypeUnknown, type);
  41. // As of writing dragged file system is the only filesystem which could have
  42. // multiple top-level paths.
  43. return type != kFileSystemTypeDragged;
  44. }
  45. static base::LazyInstance<IsolatedContext>::Leaky g_isolated_context =
  46. LAZY_INSTANCE_INITIALIZER;
  47. } // namespace
  48. IsolatedContext::FileInfoSet::FileInfoSet() = default;
  49. IsolatedContext::FileInfoSet::~FileInfoSet() = default;
  50. bool IsolatedContext::FileInfoSet::AddPath(const base::FilePath& path,
  51. std::string* registered_name) {
  52. // The given path should not contain any '..' and should be absolute.
  53. if (path.ReferencesParent() || !path.IsAbsolute())
  54. return false;
  55. base::FilePath::StringType name = GetRegisterNameForPath(path);
  56. std::string utf8name = base::FilePath(name).AsUTF8Unsafe();
  57. base::FilePath normalized_path = path.NormalizePathSeparators();
  58. bool inserted =
  59. fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
  60. if (!inserted) {
  61. int suffix = 1;
  62. std::string basepart =
  63. base::FilePath(name).RemoveExtension().AsUTF8Unsafe();
  64. std::string ext =
  65. base::FilePath(base::FilePath(name).Extension()).AsUTF8Unsafe();
  66. while (!inserted) {
  67. utf8name = base::StringPrintf("%s (%d)", basepart.c_str(), suffix++);
  68. if (!ext.empty())
  69. utf8name.append(ext);
  70. inserted =
  71. fileset_.insert(MountPointInfo(utf8name, normalized_path)).second;
  72. }
  73. }
  74. if (registered_name)
  75. *registered_name = utf8name;
  76. return true;
  77. }
  78. bool IsolatedContext::FileInfoSet::AddPathWithName(const base::FilePath& path,
  79. const std::string& name) {
  80. // The given path should not contain any '..' and should be absolute.
  81. if (path.ReferencesParent() || !path.IsAbsolute())
  82. return false;
  83. return fileset_.insert(MountPointInfo(name, path.NormalizePathSeparators()))
  84. .second;
  85. }
  86. //--------------------------------------------------------------------------
  87. IsolatedContext::ScopedFSHandle::ScopedFSHandle(std::string file_system_id)
  88. : file_system_id_(std::move(file_system_id)) {
  89. if (!file_system_id_.empty())
  90. IsolatedContext::GetInstance()->AddReference(file_system_id_);
  91. }
  92. IsolatedContext::ScopedFSHandle::~ScopedFSHandle() {
  93. if (!file_system_id_.empty())
  94. IsolatedContext::GetInstance()->RemoveReference(file_system_id_);
  95. }
  96. IsolatedContext::ScopedFSHandle::ScopedFSHandle(const ScopedFSHandle& other)
  97. : ScopedFSHandle(other.id()) {}
  98. IsolatedContext::ScopedFSHandle::ScopedFSHandle(ScopedFSHandle&& other)
  99. : file_system_id_(std::move(other.file_system_id_)) {
  100. // Moving from a string leaves it in a unspecified state, we need to make sure
  101. // to leave it empty, so explicitly clear it.
  102. other.file_system_id_.clear();
  103. }
  104. IsolatedContext::ScopedFSHandle& IsolatedContext::ScopedFSHandle::operator=(
  105. const ScopedFSHandle& other) {
  106. if (!file_system_id_.empty())
  107. IsolatedContext::GetInstance()->RemoveReference(file_system_id_);
  108. file_system_id_ = other.id();
  109. if (!file_system_id_.empty())
  110. IsolatedContext::GetInstance()->AddReference(file_system_id_);
  111. return *this;
  112. }
  113. IsolatedContext::ScopedFSHandle& IsolatedContext::ScopedFSHandle::operator=(
  114. ScopedFSHandle&& other) {
  115. if (!file_system_id_.empty())
  116. IsolatedContext::GetInstance()->RemoveReference(file_system_id_);
  117. file_system_id_ = std::move(other.file_system_id_);
  118. // Moving from a string leaves it in a unspecified state, we need to make sure
  119. // to leave it empty, so explicitly clear it.
  120. other.file_system_id_.clear();
  121. return *this;
  122. }
  123. //--------------------------------------------------------------------------
  124. class IsolatedContext::Instance {
  125. public:
  126. enum PathType { PLATFORM_PATH, VIRTUAL_PATH };
  127. // For a single-path isolated file system, which could be registered by
  128. // IsolatedContext::RegisterFileSystemForPath() or
  129. // IsolatedContext::RegisterFileSystemForVirtualPath().
  130. // Most of isolated file system contexts should be of this type.
  131. Instance(FileSystemType type,
  132. const std::string& filesystem_id,
  133. const MountPointInfo& file_info,
  134. PathType path_type);
  135. // For a multi-paths isolated file system. As of writing only file system
  136. // type which could have multi-paths is Dragged file system, and
  137. // could be registered by IsolatedContext::RegisterDraggedFileSystem().
  138. Instance(FileSystemType type, const std::set<MountPointInfo>& files);
  139. Instance(const Instance&) = delete;
  140. Instance& operator=(const Instance&) = delete;
  141. ~Instance();
  142. FileSystemType type() const { return type_; }
  143. const std::string& filesystem_id() const { return filesystem_id_; }
  144. const MountPointInfo& file_info() const { return file_info_; }
  145. const std::set<MountPointInfo>& files() const { return files_; }
  146. int ref_counts() const { return ref_counts_; }
  147. void AddRef() { ++ref_counts_; }
  148. void RemoveRef() { --ref_counts_; }
  149. bool ResolvePathForName(const std::string& name, base::FilePath* path) const;
  150. // Returns true if the instance is a single-path instance.
  151. bool IsSinglePathInstance() const;
  152. private:
  153. const FileSystemType type_;
  154. const std::string filesystem_id_;
  155. // For single-path instance.
  156. const MountPointInfo file_info_;
  157. const PathType path_type_;
  158. // For multiple-path instance (e.g. dragged file system).
  159. const std::set<MountPointInfo> files_;
  160. // Reference counts. Note that an isolated filesystem is created with ref==0
  161. // and will get deleted when the ref count reaches <=0.
  162. int ref_counts_;
  163. };
  164. IsolatedContext::Instance::Instance(FileSystemType type,
  165. const std::string& filesystem_id,
  166. const MountPointInfo& file_info,
  167. Instance::PathType path_type)
  168. : type_(type),
  169. filesystem_id_(filesystem_id),
  170. file_info_(file_info),
  171. path_type_(path_type),
  172. ref_counts_(0) {
  173. DCHECK(IsSinglePathIsolatedFileSystem(type_));
  174. }
  175. IsolatedContext::Instance::Instance(FileSystemType type,
  176. const std::set<MountPointInfo>& files)
  177. : type_(type), path_type_(PLATFORM_PATH), files_(files), ref_counts_(0) {
  178. DCHECK(!IsSinglePathIsolatedFileSystem(type_));
  179. }
  180. IsolatedContext::Instance::~Instance() = default;
  181. bool IsolatedContext::Instance::ResolvePathForName(const std::string& name,
  182. base::FilePath* path) const {
  183. if (IsSinglePathIsolatedFileSystem(type_)) {
  184. switch (path_type_) {
  185. case PLATFORM_PATH:
  186. *path = file_info_.path;
  187. break;
  188. case VIRTUAL_PATH:
  189. *path = base::FilePath();
  190. break;
  191. default:
  192. NOTREACHED();
  193. }
  194. return file_info_.name == name;
  195. }
  196. auto found = files_.find(MountPointInfo(name, base::FilePath()));
  197. if (found == files_.end())
  198. return false;
  199. *path = found->path;
  200. return true;
  201. }
  202. bool IsolatedContext::Instance::IsSinglePathInstance() const {
  203. return IsSinglePathIsolatedFileSystem(type_);
  204. }
  205. //--------------------------------------------------------------------------
  206. // static
  207. IsolatedContext* IsolatedContext::GetInstance() {
  208. return g_isolated_context.Pointer();
  209. }
  210. // static
  211. bool IsolatedContext::IsIsolatedType(FileSystemType type) {
  212. return type == kFileSystemTypeIsolated || type == kFileSystemTypeExternal;
  213. }
  214. std::string IsolatedContext::RegisterDraggedFileSystem(
  215. const FileInfoSet& files) {
  216. base::AutoLock locker(lock_);
  217. std::string filesystem_id = GetNewFileSystemId();
  218. instance_map_[filesystem_id] =
  219. std::make_unique<Instance>(kFileSystemTypeDragged, files.fileset());
  220. return filesystem_id;
  221. }
  222. IsolatedContext::ScopedFSHandle IsolatedContext::RegisterFileSystemForPath(
  223. FileSystemType type,
  224. const std::string& filesystem_id,
  225. const base::FilePath& path_in,
  226. std::string* register_name) {
  227. base::FilePath path(path_in.NormalizePathSeparators());
  228. if (path.ReferencesParent() || !path.IsAbsolute())
  229. return ScopedFSHandle();
  230. std::string name;
  231. if (register_name && !register_name->empty()) {
  232. name = *register_name;
  233. } else {
  234. name = base::FilePath(GetRegisterNameForPath(path)).AsUTF8Unsafe();
  235. if (register_name)
  236. register_name->assign(name);
  237. }
  238. std::string new_id;
  239. {
  240. base::AutoLock locker(lock_);
  241. new_id = GetNewFileSystemId();
  242. instance_map_[new_id] = std::make_unique<Instance>(
  243. type, filesystem_id, MountPointInfo(name, path),
  244. Instance::PLATFORM_PATH);
  245. path_to_id_map_[path].insert(new_id);
  246. }
  247. return ScopedFSHandle(new_id);
  248. }
  249. std::string IsolatedContext::RegisterFileSystemForVirtualPath(
  250. FileSystemType type,
  251. const std::string& register_name,
  252. const base::FilePath& cracked_path_prefix) {
  253. base::AutoLock locker(lock_);
  254. base::FilePath path(cracked_path_prefix.NormalizePathSeparators());
  255. if (path.ReferencesParent())
  256. return std::string();
  257. std::string filesystem_id = GetNewFileSystemId();
  258. instance_map_[filesystem_id] = std::make_unique<Instance>(
  259. type,
  260. std::string(), // filesystem_id
  261. MountPointInfo(register_name, cracked_path_prefix),
  262. Instance::VIRTUAL_PATH);
  263. path_to_id_map_[path].insert(filesystem_id);
  264. return filesystem_id;
  265. }
  266. bool IsolatedContext::HandlesFileSystemMountType(FileSystemType type) const {
  267. return type == kFileSystemTypeIsolated;
  268. }
  269. bool IsolatedContext::RevokeFileSystem(const std::string& filesystem_id) {
  270. base::AutoLock locker(lock_);
  271. return UnregisterFileSystem(filesystem_id);
  272. }
  273. bool IsolatedContext::GetRegisteredPath(const std::string& filesystem_id,
  274. base::FilePath* path) const {
  275. DCHECK(path);
  276. base::AutoLock locker(lock_);
  277. auto found = instance_map_.find(filesystem_id);
  278. if (found == instance_map_.end() || !found->second->IsSinglePathInstance())
  279. return false;
  280. *path = found->second->file_info().path;
  281. return true;
  282. }
  283. bool IsolatedContext::CrackVirtualPath(
  284. const base::FilePath& virtual_path,
  285. std::string* id_or_name,
  286. FileSystemType* type,
  287. std::string* cracked_id,
  288. base::FilePath* path,
  289. FileSystemMountOption* mount_option) const {
  290. DCHECK(id_or_name);
  291. DCHECK(path);
  292. // This should not contain any '..' references.
  293. if (virtual_path.ReferencesParent())
  294. return false;
  295. // Set the default mount option.
  296. *mount_option = FileSystemMountOption();
  297. // The virtual_path should comprise <id_or_name> and <relative_path> parts.
  298. std::vector<base::FilePath::StringType> components =
  299. virtual_path.GetComponents();
  300. if (components.size() < 1)
  301. return false;
  302. auto component_iter = components.begin();
  303. std::string fsid = base::FilePath(*component_iter++).MaybeAsASCII();
  304. if (fsid.empty())
  305. return false;
  306. base::FilePath cracked_path;
  307. {
  308. base::AutoLock locker(lock_);
  309. auto found_instance = instance_map_.find(fsid);
  310. if (found_instance == instance_map_.end())
  311. return false;
  312. *id_or_name = fsid;
  313. const Instance* instance = found_instance->second.get();
  314. if (type)
  315. *type = instance->type();
  316. if (cracked_id)
  317. *cracked_id = instance->filesystem_id();
  318. if (component_iter == components.end()) {
  319. // The virtual root case.
  320. path->clear();
  321. return true;
  322. }
  323. // *component_iter should be a name of the registered path.
  324. std::string name = base::FilePath(*component_iter++).AsUTF8Unsafe();
  325. if (!instance->ResolvePathForName(name, &cracked_path))
  326. return false;
  327. }
  328. for (; component_iter != components.end(); ++component_iter)
  329. cracked_path = cracked_path.Append(*component_iter);
  330. *path = cracked_path;
  331. return true;
  332. }
  333. FileSystemURL IsolatedContext::CrackURL(
  334. const GURL& url,
  335. const blink::StorageKey& storage_key) const {
  336. FileSystemURL filesystem_url = FileSystemURL(url, storage_key);
  337. if (!filesystem_url.is_valid())
  338. return FileSystemURL();
  339. return CrackFileSystemURL(filesystem_url);
  340. }
  341. FileSystemURL IsolatedContext::CreateCrackedFileSystemURL(
  342. const blink::StorageKey& storage_key,
  343. FileSystemType type,
  344. const base::FilePath& virtual_path) const {
  345. // TODO(https://crbug.com/1221308): function will have StorageKey param in
  346. // future CL; conversion from url::Origin is temporary
  347. return CrackFileSystemURL(FileSystemURL(storage_key, type, virtual_path));
  348. }
  349. void IsolatedContext::RevokeFileSystemByPath(const base::FilePath& path_in) {
  350. base::AutoLock locker(lock_);
  351. base::FilePath path(path_in.NormalizePathSeparators());
  352. auto ids_iter = path_to_id_map_.find(path);
  353. if (ids_iter == path_to_id_map_.end())
  354. return;
  355. for (auto& id : ids_iter->second)
  356. instance_map_.erase(id);
  357. path_to_id_map_.erase(ids_iter);
  358. }
  359. void IsolatedContext::AddReference(const std::string& filesystem_id) {
  360. base::AutoLock locker(lock_);
  361. DCHECK(instance_map_.find(filesystem_id) != instance_map_.end());
  362. instance_map_[filesystem_id]->AddRef();
  363. }
  364. void IsolatedContext::RemoveReference(const std::string& filesystem_id) {
  365. base::AutoLock locker(lock_);
  366. // This could get called for non-existent filesystem if it has been
  367. // already deleted by RevokeFileSystemByPath.
  368. auto found = instance_map_.find(filesystem_id);
  369. if (found == instance_map_.end())
  370. return;
  371. Instance* instance = found->second.get();
  372. DCHECK_GT(instance->ref_counts(), 0);
  373. instance->RemoveRef();
  374. if (instance->ref_counts() == 0) {
  375. bool deleted = UnregisterFileSystem(filesystem_id);
  376. DCHECK(deleted);
  377. }
  378. }
  379. bool IsolatedContext::GetDraggedFileInfo(
  380. const std::string& filesystem_id,
  381. std::vector<MountPointInfo>* files) const {
  382. DCHECK(files);
  383. base::AutoLock locker(lock_);
  384. auto found = instance_map_.find(filesystem_id);
  385. if (found == instance_map_.end() ||
  386. found->second->type() != kFileSystemTypeDragged)
  387. return false;
  388. files->assign(found->second->files().begin(), found->second->files().end());
  389. return true;
  390. }
  391. base::FilePath IsolatedContext::CreateVirtualRootPath(
  392. const std::string& filesystem_id) const {
  393. return base::FilePath().AppendASCII(filesystem_id);
  394. }
  395. IsolatedContext::IsolatedContext() = default;
  396. IsolatedContext::~IsolatedContext() = default;
  397. FileSystemURL IsolatedContext::CrackFileSystemURL(
  398. const FileSystemURL& url) const {
  399. if (!HandlesFileSystemMountType(url.type()))
  400. return FileSystemURL();
  401. std::string mount_name;
  402. std::string cracked_mount_name;
  403. FileSystemType cracked_type;
  404. base::FilePath cracked_path;
  405. FileSystemMountOption cracked_mount_option;
  406. if (!CrackVirtualPath(url.path(), &mount_name, &cracked_type,
  407. &cracked_mount_name, &cracked_path,
  408. &cracked_mount_option)) {
  409. return FileSystemURL();
  410. }
  411. return FileSystemURL(
  412. url.storage_key(), url.mount_type(), url.virtual_path(),
  413. !url.filesystem_id().empty() ? url.filesystem_id() : mount_name,
  414. cracked_type, cracked_path,
  415. cracked_mount_name.empty() ? mount_name : cracked_mount_name,
  416. cracked_mount_option);
  417. }
  418. bool IsolatedContext::UnregisterFileSystem(const std::string& filesystem_id) {
  419. lock_.AssertAcquired();
  420. auto found = instance_map_.find(filesystem_id);
  421. if (found == instance_map_.end())
  422. return false;
  423. Instance* instance = found->second.get();
  424. if (instance->IsSinglePathInstance()) {
  425. auto ids_iter = path_to_id_map_.find(instance->file_info().path);
  426. DCHECK(ids_iter != path_to_id_map_.end());
  427. ids_iter->second.erase(filesystem_id);
  428. if (ids_iter->second.empty())
  429. path_to_id_map_.erase(ids_iter);
  430. }
  431. instance_map_.erase(found);
  432. return true;
  433. }
  434. std::string IsolatedContext::GetNewFileSystemId() const {
  435. // Returns an arbitrary random string which must be unique in the map.
  436. lock_.AssertAcquired();
  437. uint32_t random_data[4];
  438. std::string id;
  439. do {
  440. base::RandBytes(random_data, sizeof(random_data));
  441. id = base::HexEncode(random_data, sizeof(random_data));
  442. } while (instance_map_.find(id) != instance_map_.end());
  443. return id;
  444. }
  445. } // namespace storage