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 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 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 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