You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
475 lines
14 KiB
475 lines
14 KiB
# Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
import dbus
|
|
|
|
from autotest_lib.client.cros import upstart
|
|
|
|
def _proto_to_blob(proto):
|
|
return dbus.ByteArray(proto.SerializeToString())
|
|
|
|
class SmbProvider(object):
|
|
"""
|
|
Wrapper for D-Bus calls to SmbProvider Daemon
|
|
|
|
The SmbProvider daemon handles calling the libsmbclient to communicate with
|
|
an SMB server. This class is a wrapper to the D-Bus interface to the daemon.
|
|
|
|
"""
|
|
|
|
_DBUS_SERVICE_NAME = "org.chromium.SmbProvider"
|
|
_DBUS_SERVICE_PATH = "/org/chromium/SmbProvider"
|
|
_DBUS_INTERFACE_NAME = "org.chromium.SmbProvider"
|
|
|
|
# Default timeout in seconds for D-Bus calls.
|
|
_DEFAULT_TIMEOUT = 120
|
|
|
|
# Chronos user ID.
|
|
_CHRONOS_UID = 1000
|
|
|
|
def __init__(self, bus_loop, proto_binding_location):
|
|
"""
|
|
Constructor.
|
|
|
|
Creates and D-Bus connection to smbproviderd.
|
|
|
|
@param bus_loop: Glib main loop object
|
|
@param proto_binding_location: The location of generated python bindings
|
|
for smbprovider protobufs.
|
|
|
|
"""
|
|
|
|
sys.path.append(proto_binding_location)
|
|
self._bus_loop = bus_loop
|
|
self.restart()
|
|
|
|
def restart(self):
|
|
"""
|
|
Restarts smbproviderd and rebinds to D-Bus interface.
|
|
|
|
"""
|
|
|
|
logging.info('restarting smbproviderd')
|
|
upstart.restart_job('smbproviderd')
|
|
|
|
try:
|
|
# Get the interface as Chronos since only they are allowed to send
|
|
# D-Bus messages to smbproviderd.
|
|
os.setresuid(self._CHRONOS_UID, self._CHRONOS_UID, 0)
|
|
|
|
bus = dbus.SystemBus(self._bus_loop)
|
|
proxy = bus.get_object(self._DBUS_SERVICE_NAME,
|
|
self._DBUS_SERVICE_PATH)
|
|
self._smbproviderd = dbus.Interface(proxy,
|
|
self._DBUS_INTERFACE_NAME)
|
|
|
|
finally:
|
|
os.setresuid(0, 0, 0)
|
|
|
|
def stop(self):
|
|
"""
|
|
Stops smbproviderd.
|
|
|
|
"""
|
|
|
|
logging.info('stopping smbproviderd')
|
|
|
|
try:
|
|
upstart.stop_job('smbproviderd')
|
|
|
|
finally:
|
|
self._smbproviderd = None
|
|
|
|
def mount(self, mount_path, workgroup, username, password):
|
|
"""
|
|
Mounts a share.
|
|
|
|
@param mount_path: Path of the share to mount.
|
|
@param workgroup: Workgroup for the mount.
|
|
@param username: Username for the mount.
|
|
@param password: Password for the mount.
|
|
|
|
@return A tuple with the ErrorType and the mount id returned the D-Bus
|
|
call.
|
|
|
|
"""
|
|
|
|
logging.info("Mounting: %s", mount_path)
|
|
|
|
from directory_entry_pb2 import MountOptionsProto
|
|
from directory_entry_pb2 import MountConfigProto
|
|
|
|
proto = MountOptionsProto()
|
|
proto.path = mount_path
|
|
proto.workgroup = workgroup
|
|
proto.username = username
|
|
proto.mount_config.enable_ntlm = True
|
|
|
|
with self.DataFd(password) as password_fd:
|
|
return self._smbproviderd.Mount(_proto_to_blob(proto),
|
|
dbus.types.UnixFd(password_fd),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def unmount(self, mount_id):
|
|
"""
|
|
Unmounts a share.
|
|
|
|
@param mount_id: Mount ID to be umounted.
|
|
|
|
@return: ErrorType from the returned D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Unmounting: %s", mount_id)
|
|
|
|
from directory_entry_pb2 import UnmountOptionsProto
|
|
|
|
proto = UnmountOptionsProto()
|
|
proto.mount_id = mount_id
|
|
|
|
return self._smbproviderd.Unmount(_proto_to_blob(proto))
|
|
|
|
def create_directory(self, mount_id, directory_path, recursive):
|
|
"""
|
|
Creates a directory.
|
|
|
|
@param mount_id: Mount ID corresponsding to the share.
|
|
@param directory_path: Path of the directory to read.
|
|
@param recursive: Boolean to indicate whether directories should be
|
|
created recursively.
|
|
|
|
@return: ErrorType from the returned D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Creating directory: %s", directory_path)
|
|
|
|
from directory_entry_pb2 import CreateDirectoryOptionsProto
|
|
from directory_entry_pb2 import ERROR_OK
|
|
|
|
proto = CreateDirectoryOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.directory_path = directory_path
|
|
proto.recursive = recursive
|
|
|
|
return self._smbproviderd.CreateDirectory(
|
|
_proto_to_blob(proto),
|
|
timout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
|
|
def read_directory(self, mount_id, directory_path):
|
|
"""
|
|
Reads a directory.
|
|
|
|
@param mount_id: Mount ID corresponding to the share.
|
|
@param directory_path: Path of the directory to read.
|
|
|
|
@return A tuple with the ErrorType and the DirectoryEntryListProto blob
|
|
string returned by the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Reading directory: %s", directory_path)
|
|
|
|
from directory_entry_pb2 import ReadDirectoryOptionsProto
|
|
from directory_entry_pb2 import DirectoryEntryListProto
|
|
from directory_entry_pb2 import ERROR_OK
|
|
|
|
proto = ReadDirectoryOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.directory_path = directory_path
|
|
|
|
error, entries_blob = self._smbproviderd.ReadDirectory(
|
|
_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
entries = DirectoryEntryListProto()
|
|
if error == ERROR_OK:
|
|
entries.ParseFromString(entries_blob)
|
|
|
|
return error, entries
|
|
|
|
def get_metadata(self, mount_id, entry_path):
|
|
"""
|
|
Gets metadata for an entry.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param entry_path: Path of the entry.
|
|
|
|
@return A tuple with the ErrorType and the GetMetadataEntryOptionsProto
|
|
blob string returned by the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Getting metadata for %s", entry_path)
|
|
|
|
from directory_entry_pb2 import GetMetadataEntryOptionsProto
|
|
from directory_entry_pb2 import DirectoryEntryProto
|
|
from directory_entry_pb2 import ERROR_OK
|
|
|
|
proto = GetMetadataEntryOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.entry_path = entry_path
|
|
|
|
error, entry_blob = self._smbproviderd.GetMetadataEntry(
|
|
_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
entry = DirectoryEntryProto()
|
|
if error == ERROR_OK:
|
|
entry.ParseFromString(entry_blob)
|
|
|
|
return error, entry
|
|
|
|
def open_file(self, mount_id, file_path, writeable):
|
|
"""
|
|
Opens a file.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param file_path: Path of the file to be opened.
|
|
@param writeable: Whether the file should be opened with write access.
|
|
|
|
@return A tuple with the ErrorType and the File ID of the opened file.
|
|
|
|
"""
|
|
|
|
logging.info("Opening file: %s", file_path)
|
|
|
|
from directory_entry_pb2 import OpenFileOptionsProto
|
|
|
|
proto = OpenFileOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.file_path = file_path
|
|
proto.writeable = writeable
|
|
|
|
return self._smbproviderd.OpenFile(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def close_file(self, mount_id, file_id):
|
|
"""
|
|
Closes a file.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param file_id: ID of the file to be closed.
|
|
|
|
@return ErrorType returned from the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Closing file: %s", file_id)
|
|
|
|
from directory_entry_pb2 import CloseFileOptionsProto
|
|
|
|
proto = CloseFileOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.file_id = file_id
|
|
|
|
return self._smbproviderd.CloseFile(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def read_file(self, mount_id, file_id, offset, length):
|
|
"""
|
|
Reads a file.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param file_id: ID of the file to be read.
|
|
@param offset: Offset to start reading.
|
|
@param length: Length in bytes to read.
|
|
|
|
@return A tuple with ErrorType and and a buffer containing the data
|
|
read.
|
|
|
|
"""
|
|
|
|
logging.info("Reading file: %s", file_id)
|
|
|
|
from directory_entry_pb2 import ReadFileOptionsProto
|
|
from directory_entry_pb2 import ERROR_OK
|
|
|
|
proto = ReadFileOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.file_id = file_id
|
|
proto.offset = offset
|
|
proto.length = length
|
|
|
|
error, fd = self._smbproviderd.ReadFile(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
data = ''
|
|
if error == ERROR_OK:
|
|
data = os.read(fd.take(), length)
|
|
|
|
return error, data
|
|
|
|
def create_file(self, mount_id, file_path):
|
|
"""
|
|
Creates a file.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param file_path: Path of the file to be created.
|
|
|
|
@return ErrorType returned from the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Creating file: %s", file_path)
|
|
|
|
from directory_entry_pb2 import CreateFileOptionsProto
|
|
|
|
proto = CreateFileOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.file_path = file_path
|
|
|
|
return self._smbproviderd.CreateFile(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def delete_entry(self, mount_id, entry_path, recursive):
|
|
"""
|
|
Deletes an entry.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param entry_path: Path of the entry to be deleted.
|
|
@param recursive: Boolean indicating whether the delete should be
|
|
recursive for directories.
|
|
|
|
@return ErrorType returned from the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Deleting entry: %s", entry_path)
|
|
|
|
from directory_entry_pb2 import DeleteEntryOptionsProto
|
|
|
|
proto = DeleteEntryOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.entry_path = entry_path
|
|
proto.recursive = recursive
|
|
|
|
return self._smbproviderd.DeleteEntry(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def move_entry(self, mount_id, source_path, target_path):
|
|
"""
|
|
Moves an entry from source to target destination.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param source_path: Path of the entry to be moved.
|
|
@param target_path: Path of where the entry will be moved to. Target
|
|
path must be a non-existent path.
|
|
|
|
@return ErrorType returned from the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Moving file to: %s", target_path)
|
|
|
|
from directory_entry_pb2 import MoveEntryOptionsProto
|
|
|
|
proto = MoveEntryOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.source_path = source_path
|
|
proto.target_path = target_path
|
|
|
|
return self._smbproviderd.MoveEntry(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def truncate(self, mount_id, file_path, length):
|
|
"""
|
|
Truncates a file.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param file_path: Path of the file to be truncated.
|
|
@param length: The new size of the file in bytes.
|
|
|
|
@return ErrorType returned from the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Truncating file: %s", file_path)
|
|
|
|
from directory_entry_pb2 import TruncateOptionsProto
|
|
|
|
proto = TruncateOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.file_path = file_path
|
|
proto.length = length
|
|
|
|
return self._smbproviderd.Truncate(_proto_to_blob(proto),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
def write_file(self, mount_id, file_id, offset, data):
|
|
"""
|
|
Writes data to a file.
|
|
|
|
@param mount_id: Mount ID from the mounted share.
|
|
@param file_id: ID of the file to be written to.
|
|
@param offset: Offset of the file to start writing to.
|
|
@param data: Data to be written.
|
|
|
|
@return ErrorType returned from the D-Bus call.
|
|
|
|
"""
|
|
|
|
logging.info("Writing to file: %s", file_id)
|
|
|
|
from directory_entry_pb2 import WriteFileOptionsProto
|
|
|
|
proto = WriteFileOptionsProto()
|
|
proto.mount_id = mount_id
|
|
proto.file_id = file_id
|
|
proto.offset = offset
|
|
proto.length = len(data)
|
|
|
|
with self.DataFd(data) as data_fd:
|
|
return self._smbproviderd.WriteFile(_proto_to_blob(proto),
|
|
dbus.types.UnixFd(data_fd),
|
|
timeout=self._DEFAULT_TIMEOUT,
|
|
byte_arrays=True)
|
|
|
|
class DataFd(object):
|
|
"""
|
|
Writes data into a file descriptor.
|
|
|
|
Use in a 'with' statement to automatically close the returned file
|
|
descriptor.
|
|
|
|
@param data: Data string.
|
|
|
|
@return A file descriptor (pipe) containing the data.
|
|
|
|
"""
|
|
|
|
def __init__(self, data):
|
|
self._data = data
|
|
self._read_fd = None
|
|
|
|
def __enter__(self):
|
|
"""Creates the data file descriptor."""
|
|
|
|
self._read_fd, write_fd = os.pipe()
|
|
os.write(write_fd, self._data)
|
|
os.close(write_fd)
|
|
return self._read_fd
|
|
|
|
def __exit__(self, mytype, value, traceback):
|
|
"""Closes the data file descriptor again."""
|
|
|
|
if self._read_fd:
|
|
os.close(self._read_fd)
|