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