1// Copyright 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#include "chrome/browser/chromeos/extensions/file_manager/private_api_mount.h"
6
7#include <string>
8
9#include "base/files/file_util.h"
10#include "base/format_macros.h"
11#include "chrome/browser/chromeos/drive/file_system_interface.h"
12#include "chrome/browser/chromeos/drive/file_system_util.h"
13#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
14#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
15#include "chrome/browser/chromeos/file_manager/volume_manager.h"
16#include "chrome/browser/drive/event_logger.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/common/extensions/api/file_manager_private.h"
19#include "chromeos/disks/disk_mount_manager.h"
20#include "content/public/browser/browser_thread.h"
21#include "google_apis/drive/task_util.h"
22#include "ui/shell_dialogs/selected_file_info.h"
23
24using chromeos::disks::DiskMountManager;
25using content::BrowserThread;
26namespace file_manager_private = extensions::api::file_manager_private;
27
28namespace extensions {
29
30namespace {
31
32// Does chmod o+r for the given path to ensure the file is readable from avfs.
33void EnsureReadableFilePermissionOnBlockingPool(
34    const base::FilePath& path,
35    const base::Callback<void(drive::FileError, const base::FilePath&)>&
36        callback) {
37  int mode = 0;
38  if (!base::GetPosixFilePermissions(path, &mode) ||
39      !base::SetPosixFilePermissions(path, mode | S_IROTH)) {
40    callback.Run(drive::FILE_ERROR_ACCESS_DENIED, base::FilePath());
41    return;
42  }
43  callback.Run(drive::FILE_ERROR_OK, path);
44}
45
46}  // namespace
47
48bool FileManagerPrivateAddMountFunction::RunAsync() {
49  using file_manager_private::AddMount::Params;
50  const scoped_ptr<Params> params(Params::Create(*args_));
51  EXTENSION_FUNCTION_VALIDATE(params);
52
53  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
54  if (logger) {
55    logger->Log(logging::LOG_INFO,
56                "%s[%d] called. (source: '%s')",
57                name().c_str(),
58                request_id(),
59                params->source.empty() ? "(none)" : params->source.c_str());
60  }
61  set_log_on_completion(true);
62
63  const base::FilePath path = file_manager::util::GetLocalPathFromURL(
64      render_view_host(), GetProfile(), GURL(params->source));
65
66  if (path.empty())
67    return false;
68
69  // Check if the source path is under Drive cache directory.
70  if (drive::util::IsUnderDriveMountPoint(path)) {
71    drive::FileSystemInterface* file_system =
72        drive::util::GetFileSystemByProfile(GetProfile());
73    if (!file_system)
74      return false;
75
76    // Ensure that the cache file exists.
77    const base::FilePath drive_path = drive::util::ExtractDrivePath(path);
78    file_system->GetFile(
79        drive_path,
80        base::Bind(&FileManagerPrivateAddMountFunction::RunAfterGetDriveFile,
81                   this,
82                   drive_path));
83  } else {
84    file_manager::VolumeManager* volume_manager =
85        file_manager::VolumeManager::Get(GetProfile());
86    DCHECK(volume_manager);
87
88    bool is_under_downloads = false;
89    const std::vector<file_manager::VolumeInfo> volumes =
90        volume_manager->GetVolumeInfoList();
91    for (size_t i = 0; i < volumes.size(); ++i) {
92      if (volumes[i].type == file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY &&
93          volumes[i].mount_path.IsParent(path)) {
94        is_under_downloads = true;
95        break;
96      }
97    }
98
99    if (is_under_downloads) {
100      // For files under downloads, change the file permission and make it
101      // readable from avfs/fuse if needed.
102      BrowserThread::PostBlockingPoolTask(
103          FROM_HERE,
104          base::Bind(&EnsureReadableFilePermissionOnBlockingPool,
105                     path,
106                     google_apis::CreateRelayCallback(
107                         base::Bind(&FileManagerPrivateAddMountFunction::
108                                        RunAfterMarkCacheFileAsMounted,
109                                    this,
110                                    path.BaseName()))));
111    } else {
112      RunAfterMarkCacheFileAsMounted(
113          path.BaseName(), drive::FILE_ERROR_OK, path);
114    }
115  }
116  return true;
117}
118
119void FileManagerPrivateAddMountFunction::RunAfterGetDriveFile(
120    const base::FilePath& drive_path,
121    drive::FileError error,
122    const base::FilePath& cache_path,
123    scoped_ptr<drive::ResourceEntry> entry) {
124  DCHECK_CURRENTLY_ON(BrowserThread::UI);
125
126  if (error != drive::FILE_ERROR_OK) {
127    SendResponse(false);
128    return;
129  }
130
131  drive::FileSystemInterface* const file_system =
132      drive::util::GetFileSystemByProfile(GetProfile());
133  if (!file_system) {
134    SendResponse(false);
135    return;
136  }
137
138  file_system->MarkCacheFileAsMounted(
139      drive_path,
140      base::Bind(
141          &FileManagerPrivateAddMountFunction::RunAfterMarkCacheFileAsMounted,
142          this,
143          drive_path.BaseName()));
144}
145
146void FileManagerPrivateAddMountFunction::RunAfterMarkCacheFileAsMounted(
147    const base::FilePath& display_name,
148    drive::FileError error,
149    const base::FilePath& file_path) {
150  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
151
152  if (error != drive::FILE_ERROR_OK) {
153    SendResponse(false);
154    return;
155  }
156
157  // Pass back the actual source path of the mount point.
158  SetResult(new base::StringValue(file_path.AsUTF8Unsafe()));
159  SendResponse(true);
160
161  // MountPath() takes a std::string.
162  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
163  disk_mount_manager->MountPath(
164      file_path.AsUTF8Unsafe(),
165      base::FilePath(display_name.Extension()).AsUTF8Unsafe(),
166      display_name.AsUTF8Unsafe(),
167      chromeos::MOUNT_TYPE_ARCHIVE);
168}
169
170bool FileManagerPrivateRemoveMountFunction::RunAsync() {
171  using file_manager_private::RemoveMount::Params;
172  const scoped_ptr<Params> params(Params::Create(*args_));
173  EXTENSION_FUNCTION_VALIDATE(params);
174
175  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
176  if (logger) {
177    logger->Log(logging::LOG_INFO,
178                "%s[%d] called. (volume_id: '%s')",
179                name().c_str(),
180                request_id(),
181                params->volume_id.c_str());
182  }
183  set_log_on_completion(true);
184
185  using file_manager::VolumeManager;
186  using file_manager::VolumeInfo;
187  VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
188  DCHECK(volume_manager);
189
190  VolumeInfo volume_info;
191  if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
192    return false;
193
194  // TODO(tbarzic): Send response when callback is received, it would make more
195  // sense than remembering issued unmount requests in file manager and showing
196  // errors for them when MountCompleted event is received.
197  switch (volume_info.type) {
198    case file_manager::VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
199    case file_manager::VOLUME_TYPE_MOUNTED_ARCHIVE_FILE: {
200      DiskMountManager::GetInstance()->UnmountPath(
201          volume_info.mount_path.value(),
202          chromeos::UNMOUNT_OPTIONS_NONE,
203          DiskMountManager::UnmountPathCallback());
204      break;
205    }
206    case file_manager::VOLUME_TYPE_PROVIDED: {
207      chromeos::file_system_provider::Service* service =
208          chromeos::file_system_provider::Service::Get(GetProfile());
209      DCHECK(service);
210      // TODO(mtomasz): Pass a more detailed error than just a bool.
211      if (!service->RequestUnmount(volume_info.extension_id,
212                                   volume_info.file_system_id)) {
213        return false;
214      }
215      break;
216    }
217    default:
218      // Requested unmounting a device which is not unmountable.
219      return false;
220  }
221
222  SendResponse(true);
223  return true;
224}
225
226bool FileManagerPrivateGetVolumeMetadataListFunction::RunAsync() {
227  if (args_->GetSize())
228    return false;
229
230  const std::vector<file_manager::VolumeInfo>& volume_info_list =
231      file_manager::VolumeManager::Get(GetProfile())->GetVolumeInfoList();
232
233  std::string log_string;
234  std::vector<linked_ptr<file_manager_private::VolumeMetadata> > result;
235  for (size_t i = 0; i < volume_info_list.size(); ++i) {
236    linked_ptr<file_manager_private::VolumeMetadata> volume_metadata(
237        new file_manager_private::VolumeMetadata);
238    file_manager::util::VolumeInfoToVolumeMetadata(
239        GetProfile(), volume_info_list[i], volume_metadata.get());
240    result.push_back(volume_metadata);
241    if (!log_string.empty())
242      log_string += ", ";
243    log_string += volume_info_list[i].mount_path.AsUTF8Unsafe();
244  }
245
246  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
247  if (logger) {
248    logger->Log(logging::LOG_INFO,
249                "%s[%d] succeeded. (results: '[%s]', %" PRIuS " mount points)",
250                name().c_str(), request_id(), log_string.c_str(),
251                result.size());
252  }
253
254  results_ =
255      file_manager_private::GetVolumeMetadataList::Results::Create(result);
256  SendResponse(true);
257  return true;
258}
259
260}  // namespace extensions
261