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