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