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