Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/libcli/raw/rawreadwrite.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   client file read/write routines
4
   Copyright (C) Andrew Tridgell 1994-1998
5
   Copyright (C) James Myers 2003
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
#include "includes.h"
22
#include "libcli/raw/libcliraw.h"
23
#include "libcli/raw/raw_proto.h"
24
25
0
#define SETUP_REQUEST(cmd, wct, buflen) do { \
26
0
  req = smbcli_request_setup(tree, cmd, wct, buflen); \
27
0
  if (!req) return NULL; \
28
0
} while (0)
29
30
/****************************************************************************
31
 low level read operation (async send)
32
****************************************************************************/
33
_PUBLIC_ struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms)
34
0
{
35
0
  bool bigoffset = false;
36
0
  struct smbcli_request *req = NULL;
37
38
0
  switch (parms->generic.level) {
39
0
  case RAW_READ_READBRAW:
40
0
    if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
41
0
      bigoffset = true;
42
0
    }
43
0
    SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0);
44
0
    SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.file.fnum);
45
0
    SIVAL(req->out.vwv, VWV(1), parms->readbraw.in.offset);
46
0
    SSVAL(req->out.vwv, VWV(3), parms->readbraw.in.maxcnt);
47
0
    SSVAL(req->out.vwv, VWV(4), parms->readbraw.in.mincnt);
48
0
    SIVAL(req->out.vwv, VWV(5), parms->readbraw.in.timeout);
49
0
    SSVAL(req->out.vwv, VWV(7), 0); /* reserved */
50
0
    if (bigoffset) {
51
0
      SIVAL(req->out.vwv, VWV(8),parms->readbraw.in.offset>>32);
52
0
    }
53
0
    break;
54
55
0
  case RAW_READ_LOCKREAD:
56
0
    SETUP_REQUEST(SMBlockread, 5, 0);
57
0
    SSVAL(req->out.vwv, VWV(0), parms->lockread.in.file.fnum);
58
0
    SSVAL(req->out.vwv, VWV(1), parms->lockread.in.count);
59
0
    SIVAL(req->out.vwv, VWV(2), parms->lockread.in.offset);
60
0
    SSVAL(req->out.vwv, VWV(4), parms->lockread.in.remaining);
61
0
    break;
62
63
0
  case RAW_READ_READ:
64
0
    SETUP_REQUEST(SMBread, 5, 0);
65
0
    SSVAL(req->out.vwv, VWV(0), parms->read.in.file.fnum);
66
0
    SSVAL(req->out.vwv, VWV(1), parms->read.in.count);
67
0
    SIVAL(req->out.vwv, VWV(2), parms->read.in.offset);
68
0
    SSVAL(req->out.vwv, VWV(4), parms->read.in.remaining);
69
0
    break;
70
71
0
  case RAW_READ_READX:
72
0
    if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
73
0
      bigoffset = true;
74
0
    }
75
0
    SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0);
76
0
    SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
77
0
    SSVAL(req->out.vwv, VWV(1), 0);
78
0
    SSVAL(req->out.vwv, VWV(2), parms->readx.in.file.fnum);
79
0
    SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset);
80
0
    SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt & 0xFFFF);
81
0
    SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt);
82
0
    SIVAL(req->out.vwv, VWV(7), parms->readx.in.maxcnt >> 16);
83
0
    SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining);
84
    /*
85
     * TODO: give an error when the offset is 64 bit
86
     *       and the server doesn't support it
87
     */
88
0
    if (bigoffset) {
89
0
      SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32);
90
0
    }
91
0
    if (parms->readx.in.read_for_execute) {
92
0
      uint16_t flags2 = SVAL(req->out.hdr, HDR_FLG2);
93
0
      flags2 |= FLAGS2_READ_PERMIT_EXECUTE;
94
0
      SSVAL(req->out.hdr, HDR_FLG2, flags2);
95
0
    }
96
0
    break;
97
98
0
  case RAW_READ_SMB2:
99
0
    return NULL;
100
0
  }
101
102
0
  if (!smbcli_request_send(req)) {
103
0
    smbcli_request_destroy(req);
104
0
    return NULL;
105
0
  }
106
107
0
  return req;
108
0
}
109
110
/****************************************************************************
111
 low level read operation (async recv)
112
****************************************************************************/
113
_PUBLIC_ NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms)
114
0
{
115
0
  if (!smbcli_request_receive(req) ||
116
0
      smbcli_request_is_error(req)) {
117
0
    goto failed;
118
0
  }
119
120
0
  switch (parms->generic.level) {
121
0
  case RAW_READ_READBRAW:
122
0
    parms->readbraw.out.nread = req->in.size - NBT_HDR_SIZE;
123
0
    if (parms->readbraw.out.nread >
124
0
        MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt)) {
125
0
      req->status = NT_STATUS_BUFFER_TOO_SMALL;
126
0
      goto failed;
127
0
    }
128
0
    memcpy(parms->readbraw.out.data, req->in.buffer + NBT_HDR_SIZE, parms->readbraw.out.nread);
129
0
    break;
130
131
0
  case RAW_READ_LOCKREAD:
132
0
    SMBCLI_CHECK_WCT(req, 5);
133
0
    parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0));
134
0
    if (parms->lockread.out.nread > parms->lockread.in.count ||
135
0
        !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3,
136
0
               parms->lockread.out.nread, parms->lockread.out.data)) {
137
0
      req->status = NT_STATUS_BUFFER_TOO_SMALL;
138
0
    }
139
0
    break;
140
141
0
  case RAW_READ_READ:
142
    /* there are 4 reserved words in the reply */
143
0
    SMBCLI_CHECK_WCT(req, 5);
144
0
    parms->read.out.nread = SVAL(req->in.vwv, VWV(0));
145
0
    if (parms->read.out.nread > parms->read.in.count ||
146
0
        !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3,
147
0
               parms->read.out.nread, parms->read.out.data)) {
148
0
      req->status = NT_STATUS_BUFFER_TOO_SMALL;
149
0
    }
150
0
    break;
151
152
0
  case RAW_READ_READX:
153
    /* there are 5 reserved words in the reply */
154
0
    SMBCLI_CHECK_WCT(req, 12);
155
0
    parms->readx.out.remaining       = SVAL(req->in.vwv, VWV(2));
156
0
    parms->readx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
157
0
    parms->readx.out.nread = SVAL(req->in.vwv, VWV(5));
158
0
    parms->readx.out.flags2 = req->flags2;
159
0
    parms->readx.out.data_offset = SVAL(req->in.vwv, VWV(6));
160
161
    /* handle oversize replies for non-chained readx replies with
162
       CAP_LARGE_READX. The snia spec has must to answer for. */
163
0
    if ((req->tree->session->transport->negotiate.capabilities & CAP_LARGE_READX)
164
0
        && CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE &&
165
0
        req->in.size >= 0x10000) {
166
0
      parms->readx.out.nread += (SVAL(req->in.vwv, VWV(7)) << 16);
167
0
      if (req->in.hdr + SVAL(req->in.vwv, VWV(6)) +
168
0
          parms->readx.out.nread <=
169
0
          req->in.buffer + req->in.size) {
170
0
        req->in.data_size += (SVAL(req->in.vwv, VWV(7)) << 16);
171
172
        /* update the bufinfo with the new size */
173
0
        smb_setup_bufinfo(req);
174
0
      }
175
0
    }
176
177
0
    if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) ||
178
0
        !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)),
179
0
               parms->readx.out.nread,
180
0
               parms->readx.out.data)) {
181
0
      req->status = NT_STATUS_BUFFER_TOO_SMALL;
182
0
    }
183
0
    break;
184
185
0
  case RAW_READ_SMB2:
186
0
    req->status = NT_STATUS_INTERNAL_ERROR;
187
0
    break;
188
0
  }
189
190
0
failed:
191
0
  return smbcli_request_destroy(req);
192
0
}
193
194
/****************************************************************************
195
 low level read operation (sync interface)
196
****************************************************************************/
197
_PUBLIC_ NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms)
198
0
{
199
0
  struct smbcli_request *req = smb_raw_read_send(tree, parms);
200
0
  return smb_raw_read_recv(req, parms);
201
0
}
202
203
204
/****************************************************************************
205
 raw write interface (async send)
206
****************************************************************************/
207
_PUBLIC_ struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms)
208
0
{
209
0
  bool bigoffset = false;
210
0
  struct smbcli_request *req = NULL;
211
212
0
  switch (parms->generic.level) {
213
0
  case RAW_WRITE_WRITEUNLOCK:
214
0
    SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count);
215
0
    SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.file.fnum);
216
0
    SSVAL(req->out.vwv, VWV(1), parms->writeunlock.in.count);
217
0
    SIVAL(req->out.vwv, VWV(2), parms->writeunlock.in.offset);
218
0
    SSVAL(req->out.vwv, VWV(4), parms->writeunlock.in.remaining);
219
0
    SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
220
0
    SSVAL(req->out.data, 1, parms->writeunlock.in.count);
221
0
    if (parms->writeunlock.in.count > 0) {
222
0
      memcpy(req->out.data+3, parms->writeunlock.in.data,
223
0
             parms->writeunlock.in.count);
224
0
    }
225
0
    break;
226
227
0
  case RAW_WRITE_WRITE:
228
0
    SETUP_REQUEST(SMBwrite, 5,  3 + parms->write.in.count);
229
0
    SSVAL(req->out.vwv, VWV(0), parms->write.in.file.fnum);
230
0
    SSVAL(req->out.vwv, VWV(1), parms->write.in.count);
231
0
    SIVAL(req->out.vwv, VWV(2), parms->write.in.offset);
232
0
    SSVAL(req->out.vwv, VWV(4), parms->write.in.remaining);
233
0
    SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
234
0
    SSVAL(req->out.data, 1, parms->write.in.count);
235
0
    if (parms->write.in.count > 0) {
236
0
      memcpy(req->out.data+3, parms->write.in.data, parms->write.in.count);
237
0
    }
238
0
    break;
239
240
0
  case RAW_WRITE_WRITECLOSE:
241
0
    SETUP_REQUEST(SMBwriteclose, 6, 1 + parms->writeclose.in.count);
242
0
    SSVAL(req->out.vwv, VWV(0), parms->writeclose.in.file.fnum);
243
0
    SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count);
244
0
    SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset);
245
0
    raw_push_dos_date3(tree->session->transport,
246
0
          req->out.vwv, VWV(4), parms->writeclose.in.mtime);
247
0
    SCVAL(req->out.data, 0, 0);
248
0
    if (parms->writeclose.in.count > 0) {
249
0
      memcpy(req->out.data+1, parms->writeclose.in.data,
250
0
             parms->writeclose.in.count);
251
0
    }
252
0
    break;
253
254
0
  case RAW_WRITE_WRITEX:
255
0
    if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
256
0
      bigoffset = true;
257
0
    }
258
0
    SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count);
259
0
    SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
260
0
    SSVAL(req->out.vwv, VWV(1), 0);
261
0
    SSVAL(req->out.vwv, VWV(2), parms->writex.in.file.fnum);
262
0
    SIVAL(req->out.vwv, VWV(3), parms->writex.in.offset);
263
0
    SIVAL(req->out.vwv, VWV(5), 0); /* reserved */
264
0
    SSVAL(req->out.vwv, VWV(7), parms->writex.in.wmode);
265
0
    SSVAL(req->out.vwv, VWV(8), parms->writex.in.remaining);
266
0
    SSVAL(req->out.vwv, VWV(9), parms->writex.in.count>>16);
267
0
    SSVAL(req->out.vwv, VWV(10), parms->writex.in.count);
268
0
    SSVAL(req->out.vwv, VWV(11), PTR_DIFF(req->out.data, req->out.hdr));
269
0
    if (bigoffset) {
270
0
            SIVAL(req->out.vwv,VWV(12),parms->writex.in.offset>>32);
271
0
    }
272
0
          if (parms->writex.in.count > 0) {
273
0
      memcpy(req->out.data, parms->writex.in.data, parms->writex.in.count);
274
0
    }
275
0
    break;
276
277
0
  case RAW_WRITE_SPLWRITE:
278
0
    SETUP_REQUEST(SMBsplwr, 1, parms->splwrite.in.count);
279
0
    SSVAL(req->out.vwv, VWV(0), parms->splwrite.in.file.fnum);
280
0
    if (parms->splwrite.in.count > 0) {
281
0
      memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count);
282
0
    }
283
0
    break;
284
285
0
  case RAW_WRITE_SMB2:
286
0
    return NULL;
287
0
  }
288
289
0
  if (!smbcli_request_send(req)) {
290
0
    smbcli_request_destroy(req);
291
0
    return NULL;
292
0
  }
293
294
0
  return req;
295
0
}
296
297
298
/****************************************************************************
299
 raw write interface (async recv)
300
****************************************************************************/
301
_PUBLIC_ NTSTATUS smb_raw_write_recv(struct smbcli_request *req, union smb_write *parms)
302
0
{
303
0
  if (!smbcli_request_receive(req) ||
304
0
      !NT_STATUS_IS_OK(req->status)) {
305
0
    goto failed;
306
0
  }
307
308
0
  switch (parms->generic.level) {
309
0
  case RAW_WRITE_WRITEUNLOCK:
310
0
    SMBCLI_CHECK_WCT(req, 1);
311
0
    parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0));
312
0
    break;
313
0
  case RAW_WRITE_WRITE:
314
0
    SMBCLI_CHECK_WCT(req, 1);
315
0
    parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0));
316
0
    break;
317
0
  case RAW_WRITE_WRITECLOSE:
318
0
    SMBCLI_CHECK_WCT(req, 1);
319
0
    parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0));
320
0
    break;
321
0
  case RAW_WRITE_WRITEX:
322
0
    SMBCLI_CHECK_WCT(req, 6);
323
0
    parms->writex.out.nwritten  = SVAL(req->in.vwv, VWV(2));
324
0
    parms->writex.out.nwritten += (CVAL(req->in.vwv, VWV(4)) << 16);
325
0
    parms->writex.out.remaining = SVAL(req->in.vwv, VWV(3));
326
0
    break;
327
0
  case RAW_WRITE_SPLWRITE:
328
0
    break;
329
0
  case RAW_WRITE_SMB2:
330
0
    req->status = NT_STATUS_INTERNAL_ERROR;
331
0
    break;
332
0
  }
333
334
0
failed:
335
0
  return smbcli_request_destroy(req);
336
0
}
337
338
/****************************************************************************
339
 raw write interface (sync interface)
340
****************************************************************************/
341
_PUBLIC_ NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms)
342
0
{
343
0
  struct smbcli_request *req = smb_raw_write_send(tree, parms);
344
0
  return smb_raw_write_recv(req, parms);
345
0
}