1/*
2 * Copyright (c) 2008 - 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "smbclient.h"
25#include "smbclient_private.h"
26#include "ntstatus.h"
27
28#include <netsmb/smbio.h>
29#include <netsmb/upi_mbuf.h>
30#include <netsmb/smb_lib.h>
31#include <sys/smb_byte_order.h>
32#include <sys/mchain.h>
33#include <netsmb/rq.h>
34#include <netsmb/smb_converter.h>
35#include <netsmb/smbio_2.h>
36
37/*
38 * Note: These are the user space APIs into the SMB client.  They take in
39 * args and calls the coresponding function smbio_ to do the ioctl call
40 * into the kernel and parse out the results.
41 */
42
43/*
44 * Forward declare SMBQueryDir since its declared in smbclient_internal.h
45 * If we ever make this public and move it into smbclient.h, then we can
46 * remove this forward declaration.
47 */
48NTSTATUS SMBQueryDir(SMBHANDLE       inConnection,
49                     uint8_t         file_info_class,
50                     uint8_t         flags,
51                     uint32_t        file_index,
52                     SMBFID          fid,
53                     const char *    name,
54                     uint32_t        name_len,
55                     char *          rcv_output_buffer,
56                     uint32_t        rcv_max_output_len,
57                     uint32_t *      rcv_output_len,
58                     uint32_t *      query_dir_reply_len);
59
60
61static NTSTATUS
62SMBMapError (int error)
63{
64    /* Is it a Posix error? If so, map it to a NT status */
65    if ((error >= EPERM) && (error <= ELAST)) {
66        /* remap it to NT status */
67        switch (error) {
68            case EOVERFLOW:
69                return STATUS_BUFFER_OVERFLOW;
70            case ENOMEM:
71                return STATUS_NO_MEMORY;
72            case EINVAL:
73                return STATUS_INVALID_PARAMETER;
74            case ENOTSUP:
75                return STATUS_NOT_IMPLEMENTED;
76            case ENOENT:
77                return STATUS_OBJECT_NAME_NOT_FOUND;
78            default:
79                smb_log_info("%s: unmapped syserr = %s",
80                             ASL_LEVEL_DEBUG,
81                             __FUNCTION__,
82                             strerror(error));
83                return STATUS_UNSUCCESSFUL;
84        }
85    }
86
87    /* Must already be a NT Status */
88    return (error);
89}
90
91
92NTSTATUS
93SMBCreateFile(
94    SMBHANDLE   inConnection,
95    const char * lpFileName,
96    uint32_t    dwDesiredAccess,
97    uint32_t    dwShareMode,
98    void *      lpSecurityAttributes,
99    uint32_t    dwCreateDisposition,
100    uint32_t    dwFlagsAndAttributes,
101    SMBFID *    phFile)
102{
103#pragma unused(lpSecurityAttributes)
104	struct open_inparms inparms;
105 	void *          hContext;
106	SMBFID			fid;
107	int             err;
108	NTSTATUS        status;
109
110    status = SMBServerContext(inConnection, &hContext);
111	if (!NT_SUCCESS(status)) {
112        return status;
113    }
114	memset(&inparms, 0, sizeof(inparms));
115	inparms.rights = dwDesiredAccess;
116	inparms.attrs = SMB_EFA_NORMAL;
117	inparms.shareMode = dwShareMode;
118	inparms.disp = dwCreateDisposition;
119	inparms.createOptions = dwFlagsAndAttributes;
120
121	err = smb2io_ntcreatex(hContext, lpFileName, NULL, &inparms, NULL, &fid);
122    status = SMBMapError(err);
123	if (!NT_SUCCESS(status)) {
124		return status;
125    }
126
127   *phFile = fid;
128    return STATUS_SUCCESS;
129}
130
131
132NTSTATUS
133SMBCreateNamedStreamFile(
134    SMBHANDLE   inConnection,
135    const char * lpFileName,
136    const char * lpFileStreamName,
137    uint32_t    dwDesiredAccess,
138    uint32_t    dwShareMode,
139    void *      lpSecurityAttributes,
140    uint32_t    dwCreateDisposition,
141    uint32_t    dwFlagsAndAttributes,
142    SMBFID *    phFile)
143{
144#pragma unused(lpSecurityAttributes)
145	struct open_inparms inparms;
146 	void *          hContext;
147	SMBFID          fid;
148	int             err;
149	NTSTATUS        status;
150
151	status = SMBServerContext(inConnection, &hContext);
152	if (!NT_SUCCESS(status)) {
153		return status;
154	}
155	memset(&inparms, 0, sizeof(inparms));
156	inparms.rights = dwDesiredAccess;
157	inparms.attrs = SMB_EFA_NORMAL;
158	inparms.shareMode = dwShareMode;
159	inparms.disp = dwCreateDisposition;
160	inparms.createOptions = dwFlagsAndAttributes;
161
162	err = smb2io_ntcreatex(hContext, lpFileName, lpFileStreamName, &inparms,
163                           NULL, &fid);
164    status = SMBMapError(err);
165	if (!NT_SUCCESS(status)) {
166		return status;
167    }
168
169	*phFile = fid;
170    return STATUS_SUCCESS;
171}
172
173NTSTATUS
174SMBTransactMailSlot(
175	SMBHANDLE   inConnection,
176	const char	 *MailSlot,
177	const void *sndParamBuffer,
178	size_t		sndParamBufferSize,
179	void		*rcvParamBuffer,
180	size_t		*rcvParamBufferSize,
181	void		*rcvDataBuffer,
182	size_t		*rcvDataBufferSize)
183{
184    NTSTATUS status;
185    void * hContext;
186	int error;
187
188    status = SMBServerContext(inConnection, &hContext);
189	if (!NT_SUCCESS(status)) {
190        return status;
191    }
192
193    error = smbio_transact(hContext, NULL, 0, MailSlot,
194						 sndParamBuffer, sndParamBufferSize,
195						 NULL, 0,
196						 rcvParamBuffer, rcvParamBufferSize,
197						 rcvDataBuffer, rcvDataBufferSize);
198    if (error) {
199		errno = error;
200        /* XXX map real NTSTATUS code */
201        return (error == EOVERFLOW) ? STATUS_BUFFER_OVERFLOW : STATUS_UNEXPECTED_IO_ERROR;
202    }
203
204    return STATUS_SUCCESS;
205}
206
207NTSTATUS
208SMBTransactNamedPipe(
209    SMBHANDLE   inConnection,
210    SMBFID		hNamedPipe,
211    const void *inBuffer,
212    size_t		inBufferSize,
213    void *		outBuffer,
214    size_t		outBufferSize,
215    size_t *	bytesRead)
216{
217    int error;
218    NTSTATUS status;
219    void * hContext;
220 	const char	*namePipe = "\\PIPE\\"; /* Always just pipe for us */
221    uint64_t    setup[2];
222
223    status = SMBServerContext(inConnection, &hContext);
224	if (!NT_SUCCESS(status)) {
225        return status;
226    }
227    setup[0] = TRANS_TRANSACT_NAMED_PIPE;
228    setup[1] = hNamedPipe;
229
230    error = smb2io_transact(hContext, setup, 2, namePipe, NULL, 0,
231                            inBuffer, inBufferSize, NULL, NULL,
232                            outBuffer, &outBufferSize);
233    status = SMBMapError(error);
234	if (!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW)) {
235		return status;
236    }
237
238    *bytesRead = outBufferSize;
239    return STATUS_SUCCESS;
240}
241
242NTSTATUS
243SMBReadFile(
244    SMBHANDLE   inConnection,
245    SMBFID      hFile,
246    void *      lpBuffer,
247    off_t       nOffset,
248    size_t       nNumberOfBytesToRead,
249    size_t		*lpNumberOfBytesRead)
250{
251    void * hContext;
252    NTSTATUS status;
253    int err;
254    uint32_t bytes_read = 0;
255
256    status = SMBServerContext(inConnection, &hContext);
257	if (!NT_SUCCESS(status)) {
258        return status;
259    }
260
261    err = smb2io_read(hContext, hFile, nOffset,
262                      (uint32_t) nNumberOfBytesToRead, (char *) lpBuffer,
263                      &bytes_read);
264    status = SMBMapError(err);
265	if (!NT_SUCCESS(status)) {
266		return status;
267    }
268
269    *lpNumberOfBytesRead = bytes_read;
270    return STATUS_SUCCESS;
271}
272
273NTSTATUS
274SMBDeviceIoControl(
275    SMBHANDLE   inConnection,
276    SMBFID      hDevice,
277    uint32_t    dwIoControlCode,
278    const void *lpInBuffer,
279    size_t		nInBufferSize,
280    void *      lpOutBuffer,
281    size_t		nOutBufferSize,
282    size_t *    lpBytesReturned)
283{
284    struct smb_ctx * ctx;
285    struct smbioc_fsctl args;
286    NTSTATUS status;
287
288    status = SMBServerContext(inConnection, (void **)&ctx);
289	if (!NT_SUCCESS(status)) {
290        return status;
291    }
292
293    if (hDevice > UINT16_MAX) {
294        /* No large file handle support until SMB 2/3 */
295        return STATUS_INVALID_HANDLE;
296    }
297
298	if (nInBufferSize > INT_MAX || nOutBufferSize > INT_MAX) {
299        return STATUS_INVALID_PARAMETER;
300	}
301
302	bzero(&args, sizeof(args));
303	args.ioc_version = SMB_IOC_STRUCT_VERSION;
304	args.ioc_fh = (smbfh)hDevice;
305	args.ioc_fsctl = dwIoControlCode;
306	args.ioc_tdatacnt = (uint32_t)nInBufferSize;
307	args.ioc_rdatacnt = (uint32_t)nOutBufferSize;
308	args.ioc_tdata = (void *)lpInBuffer;
309	args.ioc_rdata = (void *)lpOutBuffer;
310
311	if (smb_ioctl_call(ctx->ct_fd, SMBIOC_FSCTL, &args) == -1) {
312        /* XXX need to map errno */
313        return STATUS_INVALID_DEVICE_REQUEST;
314	}
315
316	if (NT_SUCCESS(args.ioc_ntstatus)) {
317		if (args.ioc_errno) {
318			/* XXX need to map errno */
319			return STATUS_UNEXPECTED_NETWORK_ERROR;
320		}
321
322		if (lpBytesReturned) {
323			/* Report updated response data size if the caller wants it. */
324			*lpBytesReturned = (size_t)args.ioc_rdatacnt;
325		}
326	}
327    return args.ioc_ntstatus;
328}
329
330NTSTATUS
331SMBWriteFile(
332    SMBHANDLE   inConnection,
333    SMBFID      hFile,
334    const void *lpBuffer,
335    off_t       nOffset,
336    size_t		nNumberOfBytesToWrite,
337    size_t		*lpNumberOfBytesWritten)
338{
339    void * hContext;
340    NTSTATUS status;
341    int err;
342    uint32_t bytes_written = 0;
343
344    status = SMBServerContext(inConnection, &hContext);
345	if (!NT_SUCCESS(status)) {
346        return status;
347    }
348
349    err = smb2io_write(hContext, hFile, nOffset,
350                       (uint32_t) nNumberOfBytesToWrite, (char *) lpBuffer,
351                       &bytes_written);
352    status = SMBMapError(err);
353	if (!NT_SUCCESS(status)) {
354		return status;
355    }
356
357    *lpNumberOfBytesWritten = bytes_written;
358    return STATUS_SUCCESS;
359}
360
361NTSTATUS
362SMBCloseFile(
363    SMBHANDLE   inConnection,
364    SMBFID      hFile)
365{
366    void *          hContext;
367    NTSTATUS        status;
368    int             err;
369
370    status = SMBServerContext(inConnection, &hContext);
371	if (!NT_SUCCESS(status)) {
372        return status;
373    }
374
375	err = smb2io_close_file(hContext, hFile);
376    status = SMBMapError(err);
377	if (!NT_SUCCESS(status)) {
378		return status;
379    }
380
381    return STATUS_SUCCESS;
382}
383
384NTSTATUS
385SMBQueryDir(
386    SMBHANDLE       inConnection,
387    uint8_t         file_info_class,
388    uint8_t         flags,
389    uint32_t        file_index,
390    SMBFID          fid,
391    const char *    name,
392    uint32_t        name_len,
393    char *          rcv_output_buffer,
394    uint32_t        rcv_max_output_len,
395    uint32_t *      rcv_output_len,
396    uint32_t *      query_dir_reply_len)
397{
398    int error;
399    NTSTATUS status;
400    void * hContext;
401
402    status = SMBServerContext(inConnection, &hContext);
403	if (!NT_SUCCESS(status)) {
404        return status;
405    }
406
407    error = smb2io_query_dir(hContext,
408                             file_info_class,
409                             flags,
410                             file_index,
411                             fid,
412                             name, name_len,
413                             rcv_output_buffer, rcv_max_output_len,
414                             rcv_output_len, query_dir_reply_len);
415    status = SMBMapError(error);
416	if (!NT_SUCCESS(status)) {
417		return status;
418    }
419
420    return STATUS_SUCCESS;
421}
422
423/* vim: set sw=4 ts=4 tw=79 et: */
424