• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The Chromium OS 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
5import logging
6import os
7import sys
8
9import dbus
10
11from autotest_lib.client.cros import upstart
12
13def _proto_to_blob(proto):
14    return dbus.ByteArray(proto.SerializeToString())
15
16class SmbProvider(object):
17    """
18    Wrapper for D-Bus calls to SmbProvider Daemon
19
20    The SmbProvider daemon handles calling the libsmbclient to communicate with
21    an SMB server. This class is a wrapper to the D-Bus interface to the daemon.
22
23    """
24
25    _DBUS_SERVICE_NAME = "org.chromium.SmbProvider"
26    _DBUS_SERVICE_PATH = "/org/chromium/SmbProvider"
27    _DBUS_INTERFACE_NAME = "org.chromium.SmbProvider"
28
29    # Default timeout in seconds for D-Bus calls.
30    _DEFAULT_TIMEOUT = 120
31
32    # Chronos user ID.
33    _CHRONOS_UID = 1000
34
35    def __init__(self, bus_loop, proto_binding_location):
36        """
37        Constructor.
38
39        Creates and D-Bus connection to smbproviderd.
40
41        @param bus_loop: Glib main loop object
42        @param proto_binding_location: The location of generated python bindings
43        for smbprovider protobufs.
44
45        """
46
47        sys.path.append(proto_binding_location)
48        self._bus_loop = bus_loop
49        self.restart()
50
51    def restart(self):
52        """
53        Restarts smbproviderd and rebinds to D-Bus interface.
54
55        """
56
57        logging.info('restarting smbproviderd')
58        upstart.restart_job('smbproviderd')
59
60        try:
61            # Get the interface as Chronos since only they are allowed to send
62            # D-Bus messages to smbproviderd.
63            os.setresuid(self._CHRONOS_UID, self._CHRONOS_UID, 0)
64
65            bus = dbus.SystemBus(self._bus_loop)
66            proxy = bus.get_object(self._DBUS_SERVICE_NAME,
67                                   self._DBUS_SERVICE_PATH)
68            self._smbproviderd = dbus.Interface(proxy,
69                                                self._DBUS_INTERFACE_NAME)
70
71        finally:
72            os.setresuid(0, 0, 0)
73
74    def stop(self):
75        """
76        Stops smbproviderd.
77
78        """
79
80        logging.info('stopping smbproviderd')
81
82        try:
83            upstart.stop_job('smbproviderd')
84
85        finally:
86            self._smbproviderd = None
87
88    def mount(self, mount_path, workgroup, username, password):
89        """
90        Mounts a share.
91
92        @param mount_path: Path of the share to mount.
93        @param workgroup: Workgroup for the mount.
94        @param username: Username for the mount.
95        @param password: Password for the mount.
96
97        @return A tuple with the ErrorType and the mount id returned the D-Bus
98        call.
99
100        """
101
102        logging.info("Mounting: %s", mount_path)
103
104        from directory_entry_pb2 import MountOptionsProto
105        from directory_entry_pb2 import MountConfigProto
106
107        proto = MountOptionsProto()
108        proto.path = mount_path
109        proto.workgroup = workgroup
110        proto.username = username
111        proto.mount_config.enable_ntlm = True
112
113        with self.DataFd(password) as password_fd:
114            return self._smbproviderd.Mount(_proto_to_blob(proto),
115                                            dbus.types.UnixFd(password_fd),
116                                            timeout=self._DEFAULT_TIMEOUT,
117                                            byte_arrays=True)
118
119    def unmount(self, mount_id):
120        """
121        Unmounts a share.
122
123        @param mount_id: Mount ID to be umounted.
124
125        @return: ErrorType from the returned D-Bus call.
126
127        """
128
129        logging.info("Unmounting: %s", mount_id)
130
131        from directory_entry_pb2 import UnmountOptionsProto
132
133        proto = UnmountOptionsProto()
134        proto.mount_id = mount_id
135
136        return self._smbproviderd.Unmount(_proto_to_blob(proto))
137
138    def create_directory(self, mount_id, directory_path, recursive):
139        """
140        Creates a directory.
141
142        @param mount_id: Mount ID corresponsding to the share.
143        @param directory_path: Path of the directory to read.
144        @param recursive: Boolean to indicate whether directories should be
145                created recursively.
146
147        @return: ErrorType from the returned D-Bus call.
148
149        """
150
151        logging.info("Creating directory: %s", directory_path)
152
153        from directory_entry_pb2 import CreateDirectoryOptionsProto
154        from directory_entry_pb2 import ERROR_OK
155
156        proto = CreateDirectoryOptionsProto()
157        proto.mount_id = mount_id
158        proto.directory_path = directory_path
159        proto.recursive = recursive
160
161        return self._smbproviderd.CreateDirectory(
162                _proto_to_blob(proto),
163                timout=self._DEFAULT_TIMEOUT,
164                byte_arrays=True)
165
166
167    def read_directory(self, mount_id, directory_path):
168        """
169        Reads a directory.
170
171        @param mount_id: Mount ID corresponding to the share.
172        @param directory_path: Path of the directory to read.
173
174        @return A tuple with the ErrorType and the DirectoryEntryListProto blob
175        string returned by the D-Bus call.
176
177        """
178
179        logging.info("Reading directory: %s", directory_path)
180
181        from directory_entry_pb2 import ReadDirectoryOptionsProto
182        from directory_entry_pb2 import DirectoryEntryListProto
183        from directory_entry_pb2 import ERROR_OK
184
185        proto = ReadDirectoryOptionsProto()
186        proto.mount_id = mount_id
187        proto.directory_path = directory_path
188
189        error, entries_blob = self._smbproviderd.ReadDirectory(
190                _proto_to_blob(proto),
191                timeout=self._DEFAULT_TIMEOUT,
192                byte_arrays=True)
193
194        entries = DirectoryEntryListProto()
195        if error == ERROR_OK:
196            entries.ParseFromString(entries_blob)
197
198        return error, entries
199
200    def get_metadata(self, mount_id, entry_path):
201        """
202        Gets metadata for an entry.
203
204        @param mount_id: Mount ID from the mounted share.
205        @param entry_path: Path of the entry.
206
207        @return A tuple with the ErrorType and the GetMetadataEntryOptionsProto
208        blob string returned by the D-Bus call.
209
210        """
211
212        logging.info("Getting metadata for %s", entry_path)
213
214        from directory_entry_pb2 import GetMetadataEntryOptionsProto
215        from directory_entry_pb2 import DirectoryEntryProto
216        from directory_entry_pb2 import ERROR_OK
217
218        proto = GetMetadataEntryOptionsProto()
219        proto.mount_id = mount_id
220        proto.entry_path = entry_path
221
222        error, entry_blob = self._smbproviderd.GetMetadataEntry(
223                _proto_to_blob(proto),
224                timeout=self._DEFAULT_TIMEOUT,
225                byte_arrays=True)
226
227        entry = DirectoryEntryProto()
228        if error == ERROR_OK:
229            entry.ParseFromString(entry_blob)
230
231        return error, entry
232
233    def open_file(self, mount_id, file_path, writeable):
234        """
235        Opens a file.
236
237        @param mount_id: Mount ID from the mounted share.
238        @param file_path: Path of the file to be opened.
239        @param writeable: Whether the file should be opened with write access.
240
241        @return A tuple with the ErrorType and the File ID of the opened file.
242
243        """
244
245        logging.info("Opening file: %s", file_path)
246
247        from directory_entry_pb2 import OpenFileOptionsProto
248
249        proto = OpenFileOptionsProto()
250        proto.mount_id = mount_id
251        proto.file_path = file_path
252        proto.writeable = writeable
253
254        return self._smbproviderd.OpenFile(_proto_to_blob(proto),
255                                           timeout=self._DEFAULT_TIMEOUT,
256                                           byte_arrays=True)
257
258    def close_file(self, mount_id, file_id):
259        """
260        Closes a file.
261
262        @param mount_id: Mount ID from the mounted share.
263        @param file_id: ID of the file to be closed.
264
265        @return ErrorType returned from the D-Bus call.
266
267        """
268
269        logging.info("Closing file: %s", file_id)
270
271        from directory_entry_pb2 import CloseFileOptionsProto
272
273        proto = CloseFileOptionsProto()
274        proto.mount_id = mount_id
275        proto.file_id = file_id
276
277        return self._smbproviderd.CloseFile(_proto_to_blob(proto),
278                                            timeout=self._DEFAULT_TIMEOUT,
279                                            byte_arrays=True)
280
281    def read_file(self, mount_id, file_id, offset, length):
282        """
283        Reads a file.
284
285        @param mount_id: Mount ID from the mounted share.
286        @param file_id: ID of the file to be read.
287        @param offset: Offset to start reading.
288        @param length: Length in bytes to read.
289
290        @return A tuple with ErrorType and and a buffer containing the data
291        read.
292
293        """
294
295        logging.info("Reading file: %s", file_id)
296
297        from directory_entry_pb2 import ReadFileOptionsProto
298        from directory_entry_pb2 import ERROR_OK
299
300        proto = ReadFileOptionsProto()
301        proto.mount_id = mount_id
302        proto.file_id = file_id
303        proto.offset = offset
304        proto.length = length
305
306        error, fd = self._smbproviderd.ReadFile(_proto_to_blob(proto),
307                                                timeout=self._DEFAULT_TIMEOUT,
308                                                byte_arrays=True)
309
310        data = ''
311        if error == ERROR_OK:
312            data = os.read(fd.take(), length)
313
314        return error, data
315
316    def create_file(self, mount_id, file_path):
317        """
318        Creates a file.
319
320        @param mount_id: Mount ID from the mounted share.
321        @param file_path: Path of the file to be created.
322
323        @return ErrorType returned from the D-Bus call.
324
325        """
326
327        logging.info("Creating file: %s", file_path)
328
329        from directory_entry_pb2 import CreateFileOptionsProto
330
331        proto = CreateFileOptionsProto()
332        proto.mount_id = mount_id
333        proto.file_path = file_path
334
335        return self._smbproviderd.CreateFile(_proto_to_blob(proto),
336                                             timeout=self._DEFAULT_TIMEOUT,
337                                             byte_arrays=True)
338
339    def delete_entry(self, mount_id, entry_path, recursive):
340        """
341        Deletes an entry.
342
343        @param mount_id: Mount ID from the mounted share.
344        @param entry_path: Path of the entry to be deleted.
345        @param recursive: Boolean indicating whether the delete should be
346        recursive for directories.
347
348        @return ErrorType returned from the D-Bus call.
349
350        """
351
352        logging.info("Deleting entry: %s", entry_path)
353
354        from directory_entry_pb2 import DeleteEntryOptionsProto
355
356        proto = DeleteEntryOptionsProto()
357        proto.mount_id = mount_id
358        proto.entry_path = entry_path
359        proto.recursive = recursive
360
361        return self._smbproviderd.DeleteEntry(_proto_to_blob(proto),
362                                              timeout=self._DEFAULT_TIMEOUT,
363                                              byte_arrays=True)
364
365    def move_entry(self, mount_id, source_path, target_path):
366        """
367        Moves an entry from source to target destination.
368
369        @param mount_id: Mount ID from the mounted share.
370        @param source_path: Path of the entry to be moved.
371        @param target_path: Path of where the entry will be moved to. Target
372        path must be a non-existent path.
373
374        @return ErrorType returned from the D-Bus call.
375
376        """
377
378        logging.info("Moving file to: %s", target_path)
379
380        from directory_entry_pb2 import MoveEntryOptionsProto
381
382        proto = MoveEntryOptionsProto()
383        proto.mount_id = mount_id
384        proto.source_path = source_path
385        proto.target_path = target_path
386
387        return self._smbproviderd.MoveEntry(_proto_to_blob(proto),
388                                            timeout=self._DEFAULT_TIMEOUT,
389                                            byte_arrays=True)
390
391    def truncate(self, mount_id, file_path, length):
392        """
393        Truncates a file.
394
395        @param mount_id: Mount ID from the mounted share.
396        @param file_path: Path of the file to be truncated.
397        @param length: The new size of the file in bytes.
398
399        @return ErrorType returned from the D-Bus call.
400
401        """
402
403        logging.info("Truncating file: %s", file_path)
404
405        from directory_entry_pb2 import TruncateOptionsProto
406
407        proto = TruncateOptionsProto()
408        proto.mount_id = mount_id
409        proto.file_path = file_path
410        proto.length = length
411
412        return self._smbproviderd.Truncate(_proto_to_blob(proto),
413                                           timeout=self._DEFAULT_TIMEOUT,
414                                           byte_arrays=True)
415
416    def write_file(self, mount_id, file_id, offset, data):
417        """
418        Writes data to a file.
419
420        @param mount_id: Mount ID from the mounted share.
421        @param file_id: ID of the file to be written to.
422        @param offset: Offset of the file to start writing to.
423        @param data: Data to be written.
424
425        @return ErrorType returned from the D-Bus call.
426
427        """
428
429        logging.info("Writing to file: %s", file_id)
430
431        from directory_entry_pb2 import WriteFileOptionsProto
432
433        proto = WriteFileOptionsProto()
434        proto.mount_id = mount_id
435        proto.file_id = file_id
436        proto.offset = offset
437        proto.length = len(data)
438
439        with self.DataFd(data) as data_fd:
440            return self._smbproviderd.WriteFile(_proto_to_blob(proto),
441                                                dbus.types.UnixFd(data_fd),
442                                                timeout=self._DEFAULT_TIMEOUT,
443                                                byte_arrays=True)
444
445    class DataFd(object):
446        """
447        Writes data into a file descriptor.
448
449        Use in a 'with' statement to automatically close the returned file
450        descriptor.
451
452        @param data: Data string.
453
454        @return A file descriptor (pipe) containing the data.
455
456        """
457
458        def __init__(self, data):
459            self._data = data
460            self._read_fd = None
461
462        def __enter__(self):
463            """Creates the data file descriptor."""
464
465            self._read_fd, write_fd = os.pipe()
466            os.write(write_fd, self._data)
467            os.close(write_fd)
468            return self._read_fd
469
470        def __exit__(self, mytype, value, traceback):
471            """Closes the data file descriptor again."""
472
473            if self._read_fd:
474                os.close(self._read_fd)
475