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 }