1d082c877SGordon Ross /*
2d082c877SGordon Ross  * This file and its contents are supplied under the terms of the
3d082c877SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
4d082c877SGordon Ross  * You may only use this file in accordance with the terms of version
5d082c877SGordon Ross  * 1.0 of the CDDL.
6d082c877SGordon Ross  *
7d082c877SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
8d082c877SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
9d082c877SGordon Ross  * http://www.illumos.org/license/CDDL.
10d082c877SGordon Ross  */
11d082c877SGordon Ross 
12d082c877SGordon Ross /*
13*dfa42fabSMatt Barden  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
14d082c877SGordon Ross  */
15d082c877SGordon Ross 
16d082c877SGordon Ross /*
17d082c877SGordon Ross  * Create context handler for "AAPL" extensions.
18d082c877SGordon Ross  * See: smbsrv/smb2_aapl.h for documentation.
19d082c877SGordon Ross  */
20d082c877SGordon Ross 
21d082c877SGordon Ross #include <smbsrv/smb2_kproto.h>
22d082c877SGordon Ross #include <smbsrv/smb2_aapl.h>
23d082c877SGordon Ross #include <smbsrv/smb_fsops.h>
24d082c877SGordon Ross 
25d082c877SGordon Ross /* SMB2 AAPL extensions: enabled? */
26d082c877SGordon Ross int smb2_aapl_extensions = 1;
2755f0a249SGordon Ross 
28d082c877SGordon Ross /*
2955f0a249SGordon Ross  * smb2_aapl_server_caps is a flags word containing detailed
3055f0a249SGordon Ross  * capabilities as shown in smb2_aapl.h (kAPPL_...)
3155f0a249SGordon Ross  *
3255f0a249SGordon Ross  * We actually can support OSX_COPYFILE but modern MacOS clients
3355f0a249SGordon Ross  * work better using the plain old FSCTL_SRV_COPYCHUNK ioctl, which
3455f0a249SGordon Ross  * avoids our needing to handle all the meta-data and streams.
3555f0a249SGordon Ross  *
3655f0a249SGordon Ross  * We could turn on kAAPL_UNIX_BASED below and report UNIX modes in
3755f0a249SGordon Ross  * directory listings (see smb2_aapl_get_macinfo below) but we don't
38d082c877SGordon Ross  * because the modes ZFS presents with non-trivial ACLs cause mac
39d082c877SGordon Ross  * clients to misbehave when copying files from the share to local.
40d082c877SGordon Ross  * For example, we may have a file that we can read, but which has
41d082c877SGordon Ross  * mode 0200.  When the mac copies such a file to the local disk,
42d082c877SGordon Ross  * the copy cannot be opened for read.  For now just turn off the
43d082c877SGordon Ross  * kAAPL_UNIX_BASED flag.  Later we might set this flag and return
44d082c877SGordon Ross  * modes only when we have a trivial ACL.
45d082c877SGordon Ross  */
4655f0a249SGordon Ross uint64_t smb2_aapl_server_caps =
4755f0a249SGordon Ross 	kAAPL_SUPPORTS_READ_DIR_ATTR;
4855f0a249SGordon Ross 	/* | kAAPL_SUPPORTS_OSX_COPYFILE */
4955f0a249SGordon Ross 	/* | kAAPL_UNIX_BASED; */
50d082c877SGordon Ross 
51479c108bSGordon Ross uint64_t smb2_aapl_volume_caps = kAAPL_SUPPORTS_FULL_SYNC;
52479c108bSGordon Ross 
53d082c877SGordon Ross /*
54d082c877SGordon Ross  * Normally suppress file IDs for MacOS because it
55d082c877SGordon Ross  * requires them to be unique per share, and ours
56d082c877SGordon Ross  * can have duplicates under .zfs or sub-mounts.
57d082c877SGordon Ross  */
58d082c877SGordon Ross int smb2_aapl_use_file_ids = 0;
59d082c877SGordon Ross 
60d082c877SGordon Ross static uint32_t smb2_aapl_srv_query(smb_request_t *,
61d082c877SGordon Ross 	mbuf_chain_t *, mbuf_chain_t *);
62d082c877SGordon Ross 
63d082c877SGordon Ross static int smb_aapl_ext_maxlen = 512;
64d082c877SGordon Ross 
65d082c877SGordon Ross /*
66d082c877SGordon Ross  * Decode an AAPL create context (command code) and build the
67d082c877SGordon Ross  * corresponding AAPL c.c. response.
68d082c877SGordon Ross  */
69d082c877SGordon Ross uint32_t
smb2_aapl_crctx(smb_request_t * sr,mbuf_chain_t * mbcin,mbuf_chain_t * mbcout)70d082c877SGordon Ross smb2_aapl_crctx(smb_request_t *sr,
71*dfa42fabSMatt Barden     mbuf_chain_t *mbcin,
72*dfa42fabSMatt Barden     mbuf_chain_t *mbcout)
73d082c877SGordon Ross {
74d082c877SGordon Ross 	uint32_t cmdcode;
75d082c877SGordon Ross 	uint32_t status;
76d082c877SGordon Ross 	int rc;
77d082c877SGordon Ross 
78d082c877SGordon Ross 	if (smb2_aapl_extensions == 0)
79d082c877SGordon Ross 		return (NT_STATUS_NOT_SUPPORTED);
80d082c877SGordon Ross 
81d082c877SGordon Ross 	rc = smb_mbc_decodef(mbcin, "l4.", &cmdcode);
82d082c877SGordon Ross 	if (rc != 0)
83d082c877SGordon Ross 		return (NT_STATUS_INFO_LENGTH_MISMATCH);
84d082c877SGordon Ross 	mbcout->max_bytes = smb_aapl_ext_maxlen;
85d082c877SGordon Ross 	(void) smb_mbc_encodef(mbcout, "ll", cmdcode, 0);
86d082c877SGordon Ross 
87d082c877SGordon Ross 	switch (cmdcode) {
88d082c877SGordon Ross 	case kAAPL_SERVER_QUERY:
89d082c877SGordon Ross 		status = smb2_aapl_srv_query(sr, mbcin, mbcout);
90d082c877SGordon Ross 		break;
91d082c877SGordon Ross 	case kAAPL_RESOLVE_ID:
92d082c877SGordon Ross 	default:
93d082c877SGordon Ross 		status = NT_STATUS_INVALID_INFO_CLASS;
94d082c877SGordon Ross 		break;
95d082c877SGordon Ross 	}
96d082c877SGordon Ross 
97d082c877SGordon Ross 	return (status);
98d082c877SGordon Ross }
99d082c877SGordon Ross 
100d082c877SGordon Ross /*
101d082c877SGordon Ross  * Handle an AAPL c.c. kAAPL_SERVER_QUERY
102d082c877SGordon Ross  * Return our Mac-ish capabilities.  We also need to remember
103d082c877SGordon Ross  * that this client wants AAPL readdir etc.
104d082c877SGordon Ross  * Typically see: client_bitmap=7, client_caps=7
105d082c877SGordon Ross  */
106d082c877SGordon Ross static uint32_t
smb2_aapl_srv_query(smb_request_t * sr,mbuf_chain_t * mbcin,mbuf_chain_t * mbcout)107d082c877SGordon Ross smb2_aapl_srv_query(smb_request_t *sr,
108*dfa42fabSMatt Barden     mbuf_chain_t *mbcin, mbuf_chain_t *mbcout)
109d082c877SGordon Ross {
110d082c877SGordon Ross 	uint64_t client_bitmap;
111d082c877SGordon Ross 	uint64_t client_caps;
112d082c877SGordon Ross 	uint64_t server_bitmap;
113d082c877SGordon Ross 	int rc;
114d082c877SGordon Ross 
115d082c877SGordon Ross 	rc = smb_mbc_decodef(
116d082c877SGordon Ross 	    mbcin, "qq",
117d082c877SGordon Ross 	    &client_bitmap,
118d082c877SGordon Ross 	    &client_caps);
119d082c877SGordon Ross 	if (rc != 0)
120d082c877SGordon Ross 		return (NT_STATUS_INFO_LENGTH_MISMATCH);
121d082c877SGordon Ross 
122d082c877SGordon Ross 	smb_rwx_rwenter(&sr->session->s_lock, RW_WRITER);
123d082c877SGordon Ross 
124d082c877SGordon Ross 	/* Remember that this is a MacOS client. */
125d082c877SGordon Ross 	sr->session->native_os = NATIVE_OS_MACOS;
126d082c877SGordon Ross 	sr->session->s_flags |= SMB_SSN_AAPL_CCEXT;
127d082c877SGordon Ross 
128d082c877SGordon Ross 	/*
129d082c877SGordon Ross 	 * Select which parts of the bitmap we use.
130d082c877SGordon Ross 	 */
131d082c877SGordon Ross 	server_bitmap = client_bitmap &
132d082c877SGordon Ross 	    (kAAPL_SERVER_CAPS | kAAPL_VOLUME_CAPS);
133d082c877SGordon Ross 	(void) smb_mbc_encodef(mbcout, "q", server_bitmap);
134d082c877SGordon Ross 
135d082c877SGordon Ross 	if ((server_bitmap & kAAPL_SERVER_CAPS) != 0) {
136d082c877SGordon Ross 		uint64_t server_caps =
137d082c877SGordon Ross 		    smb2_aapl_server_caps & client_caps;
138d082c877SGordon Ross 		if (server_caps & kAAPL_SUPPORTS_READ_DIR_ATTR)
139d082c877SGordon Ross 			sr->session->s_flags |= SMB_SSN_AAPL_READDIR;
140d082c877SGordon Ross 		(void) smb_mbc_encodef(mbcout, "q", server_caps);
141d082c877SGordon Ross 	}
142d082c877SGordon Ross 	if ((server_bitmap & kAAPL_VOLUME_CAPS) != 0) {
143479c108bSGordon Ross 		(void) smb_mbc_encodef(mbcout, "q", smb2_aapl_volume_caps);
144d082c877SGordon Ross 	}
145d082c877SGordon Ross 
146d082c877SGordon Ross 	/* Pad2, null model string. */
147d082c877SGordon Ross 	(void) smb_mbc_encodef(mbcout, "ll", 0, 0);
148d082c877SGordon Ross 
149d082c877SGordon Ross 	smb_rwx_rwexit(&sr->session->s_lock);
150d082c877SGordon Ross 
151d082c877SGordon Ross 	return (0);
152d082c877SGordon Ross }
153d082c877SGordon Ross 
154d082c877SGordon Ross /*
155d082c877SGordon Ross  * Get additional information about a directory entry
156d082c877SGordon Ross  * needed when MacOS is using the AAPL extensions.
157d082c877SGordon Ross  * This is called after smb_odir_read_fileinfo has
158d082c877SGordon Ross  * filled in the fileinfo.  This fills in macinfo.
159d082c877SGordon Ross  *
160d082c877SGordon Ross  * This does a couple FS operations per directory entry.
161d082c877SGordon Ross  * That has some cost, but if we don't do it for them here,
162d082c877SGordon Ross  * the client has to make two more round trips for each
163d082c877SGordon Ross  * directory entry, which is much worse.
164d082c877SGordon Ross  */
165d082c877SGordon Ross int
smb2_aapl_get_macinfo(smb_request_t * sr,smb_odir_t * od,smb_fileinfo_t * fileinfo,smb_macinfo_t * mi,char * tbuf,size_t tbuflen)166d082c877SGordon Ross smb2_aapl_get_macinfo(smb_request_t *sr, smb_odir_t *od,
167*dfa42fabSMatt Barden     smb_fileinfo_t *fileinfo, smb_macinfo_t *mi,
168*dfa42fabSMatt Barden     char *tbuf, size_t tbuflen)
169d082c877SGordon Ross {
170d082c877SGordon Ross 	int		rc;
171d082c877SGordon Ross 	cred_t		*kcr = zone_kcred();
172d082c877SGordon Ross 	smb_node_t	*fnode, *snode;
173d082c877SGordon Ross 	smb_attr_t	attr;
174d082c877SGordon Ross 	uint32_t	AfpInfo[15];
175d082c877SGordon Ross 
176d082c877SGordon Ross 	bzero(mi, sizeof (*mi));
177d082c877SGordon Ross 
178d082c877SGordon Ross 	rc = smb_fsop_lookup(sr, od->d_cred, SMB_CASE_SENSITIVE,
179d082c877SGordon Ross 	    od->d_tree->t_snode, od->d_dnode, fileinfo->fi_name, &fnode);
180d082c877SGordon Ross 	if (rc != 0)
181d082c877SGordon Ross 		return (rc);
182d082c877SGordon Ross 	/* Note: hold ref on fnode, must release */
183d082c877SGordon Ross 
184d082c877SGordon Ross 	smb_fsop_eaccess(sr, od->d_cred, fnode, &mi->mi_maxaccess);
185d082c877SGordon Ross 
186d082c877SGordon Ross 	/*
187d082c877SGordon Ross 	 * mi_rforksize
188d082c877SGordon Ross 	 * Get length of stream: "AFP_Resource"
189d082c877SGordon Ross 	 * Return size=zero if not found.
190d082c877SGordon Ross 	 */
191d082c877SGordon Ross 	(void) snprintf(tbuf, tbuflen, "%s:AFP_Resource", fileinfo->fi_name);
192d082c877SGordon Ross 	rc = smb_fsop_lookup_name(sr, kcr, 0, sr->tid_tree->t_snode,
193d082c877SGordon Ross 	    od->d_dnode, tbuf, &snode);
194d082c877SGordon Ross 	if (rc == 0) {
195d082c877SGordon Ross 		bzero(&attr, sizeof (attr));
196d082c877SGordon Ross 		attr.sa_mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ;
197d082c877SGordon Ross 		rc = smb_node_getattr(NULL, snode, kcr, NULL, &attr);
198d082c877SGordon Ross 		if (rc == 0) {
199d082c877SGordon Ross 			mi->mi_rforksize = attr.sa_vattr.va_size;
200d082c877SGordon Ross 		}
201d082c877SGordon Ross 		smb_node_release(snode);
202d082c877SGordon Ross 		snode = NULL;
203d082c877SGordon Ross 	}
204d082c877SGordon Ross 
205d082c877SGordon Ross 	/*
206d082c877SGordon Ross 	 * mi_finder
207d082c877SGordon Ross 	 * Get contents of stream: "AFP_AfpInfo"
208d082c877SGordon Ross 	 * read 60 bytes, copy 32 bytes at off 16
209d082c877SGordon Ross 	 */
210d082c877SGordon Ross 	(void) snprintf(tbuf, tbuflen, "%s:AFP_AfpInfo", fileinfo->fi_name);
211d082c877SGordon Ross 	rc = smb_fsop_lookup_name(sr, kcr, 0, sr->tid_tree->t_snode,
212d082c877SGordon Ross 	    od->d_dnode, tbuf, &snode);
213d082c877SGordon Ross 	if (rc == 0) {
214d082c877SGordon Ross 		iovec_t iov;
215d082c877SGordon Ross 		uio_t uio;
216d082c877SGordon Ross 
217d082c877SGordon Ross 		bzero(&AfpInfo, sizeof (AfpInfo));
218d082c877SGordon Ross 		bzero(&uio, sizeof (uio));
219d082c877SGordon Ross 
220d082c877SGordon Ross 		iov.iov_base = (void *) &AfpInfo;
221d082c877SGordon Ross 		iov.iov_len = sizeof (AfpInfo);
222d082c877SGordon Ross 		uio.uio_iov = &iov;
223d082c877SGordon Ross 		uio.uio_iovcnt = 1;
224d082c877SGordon Ross 		uio.uio_resid = sizeof (AfpInfo);
225d082c877SGordon Ross 		uio.uio_segflg = UIO_SYSSPACE;
226d082c877SGordon Ross 		uio.uio_extflg = UIO_COPY_DEFAULT;
227*dfa42fabSMatt Barden 		rc = smb_fsop_read(sr, kcr, snode, NULL, &uio, 0);
228d082c877SGordon Ross 		if (rc == 0 && uio.uio_resid == 0) {
229d082c877SGordon Ross 			bcopy(&AfpInfo[4], &mi->mi_finderinfo,
230d082c877SGordon Ross 			    sizeof (mi->mi_finderinfo));
231d082c877SGordon Ross 		}
232d082c877SGordon Ross 		smb_node_release(snode);
233d082c877SGordon Ross 		snode = NULL;
234d082c877SGordon Ross 	}
235d082c877SGordon Ross 
236d082c877SGordon Ross 	/*
237d082c877SGordon Ross 	 * Later: Fill in the mode if we have a trivial ACL
238d082c877SGordon Ross 	 * (otherwise leaving it zero as we do now).
239d082c877SGordon Ross 	 */
240d082c877SGordon Ross 	if (smb2_aapl_server_caps & kAAPL_UNIX_BASED) {
241d082c877SGordon Ross 		bzero(&attr, sizeof (attr));
242d082c877SGordon Ross 		attr.sa_mask = SMB_AT_MODE;
243d082c877SGordon Ross 		rc = smb_node_getattr(NULL, fnode, kcr, NULL, &attr);
244d082c877SGordon Ross 		if (rc == 0) {
245d082c877SGordon Ross 			mi->mi_unixmode = (uint16_t)attr.sa_vattr.va_mode;
246d082c877SGordon Ross 		}
247d082c877SGordon Ross 	}
248d082c877SGordon Ross 
249d082c877SGordon Ross 	smb_node_release(fnode);
250d082c877SGordon Ross 	return (0);
251d082c877SGordon Ross }
252