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