1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * File Change Notification (FCN) 29 * SMB1 specific part. 30 */ 31 32 /* 33 * SMB: nt_transact_notify_change 34 * 35 * Client Setup Words Description 36 * ================================== ================================= 37 * 38 * ULONG CompletionFilter; Specifies operation to monitor 39 * USHORT Fid; Fid of directory to monitor 40 * BOOLEAN WatchTree; TRUE = watch all subdirectories too 41 * UCHAR Reserved; MBZ 42 * 43 * This command notifies the client when the directory specified by Fid is 44 * modified. See smb_notify.c for details. 45 * 46 * The MaxParameterCount field in the NT transact header determines 47 * the size of the buffer used to return change information: 48 * 49 * Server Response Description 50 * ================================== ================================ 51 * ParameterCount # of bytes of change data 52 * Parameters[ ParameterCount ] FILE_NOTIFY_INFORMATION 53 * structures 54 * 55 * See smb_notify.c for details of FILE_NOTIFY_INFORMATION 56 */ 57 58 #include <smbsrv/smb_kproto.h> 59 60 /* 61 * smb_nt_transact_notify_change 62 * 63 * Handle and SMB NT transact NOTIFY CHANGE request. 64 * Basically, wait until "something has changed", and either 65 * return information about what changed, or return a special 66 * error telling the client "many things changed". 67 * 68 * The implementation uses a per-node list of waiting notify 69 * requests like this one, each with a blocked worker thead. 70 * Later, FEM and/or smbsrv events wake these threads, which 71 * then send the reply to the client. 72 */ 73 smb_sdrc_t 74 smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa) 75 { 76 mbuf_chain_t tmp_mbc; 77 uint32_t oBufSize; 78 uint32_t CompletionFilter; 79 unsigned char WatchTree; 80 uint32_t status; 81 82 if (smb_mbc_decodef(&xa->req_setup_mb, "lwb", 83 &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) { 84 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0); 85 return (SDRC_ERROR); 86 } 87 88 smbsr_lookup_file(sr); 89 if (sr->fid_ofile == NULL) { 90 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0); 91 return (SDRC_ERROR); 92 } 93 94 oBufSize = xa->rep_param_mb.max_bytes; 95 CompletionFilter &= FILE_NOTIFY_VALID_MASK; 96 if (WatchTree) 97 CompletionFilter |= FILE_NOTIFY_CHANGE_EV_SUBDIR; 98 99 /* 100 * Check for events and consume, non-blocking. 101 * Special return STATUS_PENDING means: 102 * No events; caller must call "act2" next. 103 */ 104 status = smb_notify_act1(sr, oBufSize, CompletionFilter); 105 if (status == NT_STATUS_PENDING) { 106 status = smb_notify_act2(sr); 107 if (status == NT_STATUS_PENDING) { 108 /* See: smb_nt_transact_notify_finish */ 109 return (SDRC_SR_KEPT); 110 } 111 /* else: some other error, or even success */ 112 } 113 114 /* 115 * SMB1 expects an empty trans response after the 116 * FID we're watching is closed. 117 */ 118 if (status == NT_STATUS_NOTIFY_CLEANUP) { 119 status = 0; 120 MBC_FLUSH(&sr->raw_data); 121 } 122 123 if (status != 0) { 124 smbsr_status(sr, status, 0, 0); 125 if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR) 126 return (SDRC_ERROR); 127 /* Else continue with NT_STATUS_NOTIFY_ENUM_DIR etc. */ 128 } 129 130 /* 131 * The nt_trans call expects the output in rep_param_mb, 132 * but our common code puts it in raw_data. Move it 133 * where the caller expects it via swaping the two, 134 * which lets the normal cleanup take care of both. 135 */ 136 tmp_mbc = xa->rep_param_mb; 137 xa->rep_param_mb = sr->raw_data; 138 sr->raw_data = tmp_mbc; 139 140 return (SDRC_SUCCESS); 141 } 142 143 /* 144 * This is called via taskq_dispatch in smb_notify.c 145 * to finish up an NT transact notify change request. 146 */ 147 void 148 smb_nt_transact_notify_finish(void *arg) 149 { 150 smb_request_t *sr = arg; 151 struct smb_xa *xa; 152 smb_disp_stats_t *sds; 153 int total_bytes, n_setup, n_param, n_data; 154 int param_off, param_pad, data_off, data_pad; 155 uint32_t status; 156 157 SMB_REQ_VALID(sr); 158 159 /* 160 * Common part of notify, puts data in sr->raw_data 161 */ 162 status = smb_notify_act3(sr); 163 164 /* 165 * SMB1 expects an empty trans response after the 166 * FID we're watching is closed. 167 */ 168 if (status == NT_STATUS_NOTIFY_CLEANUP) { 169 status = 0; 170 MBC_FLUSH(&sr->raw_data); 171 } 172 173 if (status != 0) { 174 smbsr_status(sr, status, 0, 0); 175 if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR) { 176 (void) smb_mbc_encodef(&sr->reply, "bwbw", 177 (short)0, 0L, (short)0, 0L); 178 goto sendit; 179 } 180 /* Else continue with NT_STATUS_NOTIFY_ENUM_DIR etc. */ 181 } 182 183 /* 184 * setup the NT transact reply 185 * 186 * Note that this is a copy/paste of code from 187 * smb_nt_trans_dispatch(), with minor changes. 188 * Intentionally keeping this similar to the 189 * original rather than hand-optimizing. 190 * 191 * The "setup" and "data" parts of this trans reply 192 * (n_setup, n_data, rep_setup_mb, rep_data_mb) are 193 * always empty. sr->raw_data replaces rep_param_mb. 194 */ 195 xa = sr->r_xa; 196 n_setup = MBC_LENGTH(&xa->rep_setup_mb); 197 n_param = MBC_LENGTH(&sr->raw_data); 198 n_data = MBC_LENGTH(&xa->rep_data_mb); 199 200 n_setup = (n_setup + 1) / 2; /* Convert to setup words */ 201 param_pad = 1; /* must be one */ 202 param_off = param_pad + 32 + 37 + (n_setup << 1) + 2; 203 /* Pad to 4 bytes */ 204 data_pad = (4 - ((param_off + n_param) & 3)) % 4; 205 /* Param off from hdr */ 206 data_off = param_off + n_param + data_pad; 207 total_bytes = param_pad + n_param + data_pad + n_data; 208 209 (void) smbsr_encode_result(sr, 18+n_setup, total_bytes, 210 "b3.llllllllbCw#.C#.C", 211 18 + n_setup, /* wct */ 212 n_param, /* Total Parameter Bytes */ 213 n_data, /* Total Data Bytes */ 214 n_param, /* Total Parameter Bytes this buffer */ 215 param_off, /* Param offset from header start */ 216 0, /* Param displacement */ 217 n_data, /* Total Data Bytes this buffer */ 218 data_off, /* Data offset from header start */ 219 0, /* Data displacement */ 220 n_setup, /* suwcnt */ 221 &xa->rep_setup_mb, /* setup[] */ 222 total_bytes, /* Total data bytes */ 223 param_pad, 224 &sr->raw_data, /* output mbc */ 225 data_pad, 226 &xa->rep_data_mb); 227 228 sendit: 229 DTRACE_SMB_1(op__NtTransactNotify__done2, smb_request_t *, sr); 230 231 sds = &sr->sr_server->sv_disp_stats1[sr->smb_com]; 232 atomic_add_64(&sds->sdt_txb, (int64_t)sr->reply.chain_offset); 233 234 smbsr_send_reply(sr); /* also puts the SMB header. */ 235 smbsr_cleanup(sr); 236 237 mutex_enter(&sr->sr_mutex); 238 sr->sr_state = SMB_REQ_STATE_COMPLETED; 239 mutex_exit(&sr->sr_mutex); 240 241 smb_request_free(sr); 242 }