Initial revamp of the libsmbclient interface.
[Samba/gebeck_regimport.git] / source3 / libsmb / clireadwrite.c
blobaf13ed8f7337775377fe646aa01c797c188524d5
1 /*
2 Unix SMB/CIFS implementation.
3 client file read/write routines
4 Copyright (C) Andrew Tridgell 1994-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
22 /****************************************************************************
23 Issue a single SMBread and don't wait for a reply.
24 ****************************************************************************/
26 static bool cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
27 size_t size, int i)
29 bool bigoffset = False;
31 memset(cli->outbuf,'\0',smb_size);
32 memset(cli->inbuf,'\0',smb_size);
34 if ((SMB_BIG_UINT)offset >> 32)
35 bigoffset = True;
37 cli_set_message(cli->outbuf,bigoffset ? 12 : 10,0,True);
39 SCVAL(cli->outbuf,smb_com,SMBreadX);
40 SSVAL(cli->outbuf,smb_tid,cli->cnum);
41 cli_setup_packet(cli);
43 SCVAL(cli->outbuf,smb_vwv0,0xFF);
44 SSVAL(cli->outbuf,smb_vwv2,fnum);
45 SIVAL(cli->outbuf,smb_vwv3,offset);
46 SSVAL(cli->outbuf,smb_vwv5,size);
47 SSVAL(cli->outbuf,smb_vwv6,size);
48 SSVAL(cli->outbuf,smb_vwv7,(size >> 16));
49 SSVAL(cli->outbuf,smb_mid,cli->mid + i);
51 if (bigoffset) {
52 SIVAL(cli->outbuf,smb_vwv10,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
55 return cli_send_smb(cli);
58 /****************************************************************************
59 Read size bytes at offset offset using SMBreadX.
60 ****************************************************************************/
62 ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
64 char *p;
65 size_t size2;
66 size_t readsize;
67 ssize_t total = 0;
68 /* We can only do direct reads if not signing or encrypting. */
69 bool direct_reads = !client_is_signing_on(cli) && !cli_encryption_on(cli);
71 if (size == 0)
72 return 0;
75 * Set readsize to the maximum size we can handle in one readX,
76 * rounded down to a multiple of 1024.
79 if (client_is_signing_on(cli) == false &&
80 cli_encryption_on(cli) == false &&
81 (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
82 readsize = CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
83 } else if (cli->capabilities & CAP_LARGE_READX) {
84 if (cli->is_samba) {
85 readsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
86 } else {
87 readsize = CLI_WINDOWS_MAX_LARGE_READX_SIZE;
89 } else {
90 readsize = (cli->max_xmit - (smb_size+32)) & ~1023;
93 while (total < size) {
94 readsize = MIN(readsize, size-total);
96 /* Issue a read and receive a reply */
98 if (!cli_issue_read(cli, fnum, offset, readsize, 0))
99 return -1;
101 if (direct_reads) {
102 if (!cli_receive_smb_readX_header(cli))
103 return -1;
104 } else {
105 if (!cli_receive_smb(cli))
106 return -1;
109 /* Check for error. Make sure to check for DOS and NT
110 errors. */
112 if (cli_is_error(cli)) {
113 bool recoverable_error = False;
114 NTSTATUS status = NT_STATUS_OK;
115 uint8 eclass = 0;
116 uint32 ecode = 0;
118 if (cli_is_nt_error(cli))
119 status = cli_nt_error(cli);
120 else
121 cli_dos_error(cli, &eclass, &ecode);
124 * ERRDOS ERRmoredata or STATUS_MORE_ENRTIES is a
125 * recoverable error, plus we have valid data in the
126 * packet so don't error out here.
129 if ((eclass == ERRDOS && ecode == ERRmoredata) ||
130 NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES))
131 recoverable_error = True;
133 if (!recoverable_error)
134 return -1;
137 /* size2 is the number of bytes the server returned.
138 * Might be zero. */
139 size2 = SVAL(cli->inbuf, smb_vwv5);
140 size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7))) << 16);
142 if (size2 > readsize) {
143 DEBUG(5,("server returned more than we wanted!\n"));
144 return -1;
145 } else if (size2 < 0) {
146 DEBUG(5,("read return < 0!\n"));
147 return -1;
150 if (size2) {
151 /* smb_vwv6 is the offset in the packet of the returned
152 * data bytes. Only valid if size2 != 0. */
154 if (!direct_reads) {
155 /* Copy data into buffer */
156 p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
157 memcpy(buf + total, p, size2);
158 } else {
159 /* Ensure the remaining data matches the return size. */
160 ssize_t toread = smb_len_large(cli->inbuf) - SVAL(cli->inbuf,smb_vwv6);
162 /* Ensure the size is correct. */
163 if (toread != size2) {
164 DEBUG(5,("direct read logic fail toread (%d) != size2 (%u)\n",
165 (int)toread, (unsigned int)size2 ));
166 return -1;
169 /* Read data directly into buffer */
170 toread = cli_receive_smb_data(cli,buf+total,size2);
171 if (toread != size2) {
172 DEBUG(5,("direct read read failure toread (%d) != size2 (%u)\n",
173 (int)toread, (unsigned int)size2 ));
174 return -1;
179 total += size2;
180 offset += size2;
183 * If the server returned less than we asked for we're at EOF.
186 if (size2 < readsize)
187 break;
190 return total;
193 #if 0 /* relies on client_receive_smb(), now a static in libsmb/clientgen.c */
195 /* This call is INCOMPATIBLE with SMB signing. If you remove the #if 0
196 you must fix ensure you don't attempt to sign the packets - data
197 *will* be currupted */
199 /****************************************************************************
200 Issue a single SMBreadraw and don't wait for a reply.
201 ****************************************************************************/
203 static bool cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset,
204 size_t size, int i)
207 if (!cli->sign_info.use_smb_signing) {
208 DEBUG(0, ("Cannot use readraw and SMB Signing\n"));
209 return False;
212 memset(cli->outbuf,'\0',smb_size);
213 memset(cli->inbuf,'\0',smb_size);
215 cli_set_message(cli->outbuf,10,0,True);
217 SCVAL(cli->outbuf,smb_com,SMBreadbraw);
218 SSVAL(cli->outbuf,smb_tid,cli->cnum);
219 cli_setup_packet(cli);
221 SSVAL(cli->outbuf,smb_vwv0,fnum);
222 SIVAL(cli->outbuf,smb_vwv1,offset);
223 SSVAL(cli->outbuf,smb_vwv2,size);
224 SSVAL(cli->outbuf,smb_vwv3,size);
225 SSVAL(cli->outbuf,smb_mid,cli->mid + i);
227 return cli_send_smb(cli);
230 /****************************************************************************
231 Tester for the readraw call.
232 ****************************************************************************/
234 ssize_t cli_readraw(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
236 char *p;
237 int size2;
238 size_t readsize;
239 ssize_t total = 0;
241 if (size == 0)
242 return 0;
245 * Set readsize to the maximum size we can handle in one readraw.
248 readsize = 0xFFFF;
250 while (total < size) {
251 readsize = MIN(readsize, size-total);
253 /* Issue a read and receive a reply */
255 if (!cli_issue_readraw(cli, fnum, offset, readsize, 0))
256 return -1;
258 if (!client_receive_smb(cli->fd, cli->inbuf, cli->timeout))
259 return -1;
261 size2 = smb_len(cli->inbuf);
263 if (size2 > readsize) {
264 DEBUG(5,("server returned more than we wanted!\n"));
265 return -1;
266 } else if (size2 < 0) {
267 DEBUG(5,("read return < 0!\n"));
268 return -1;
271 /* Copy data into buffer */
273 if (size2) {
274 p = cli->inbuf + 4;
275 memcpy(buf + total, p, size2);
278 total += size2;
279 offset += size2;
282 * If the server returned less than we asked for we're at EOF.
285 if (size2 < readsize)
286 break;
289 return total;
291 #endif
293 /****************************************************************************
294 Issue a single SMBwrite and don't wait for a reply.
295 ****************************************************************************/
297 static bool cli_issue_write(struct cli_state *cli,
298 int fnum,
299 off_t offset,
300 uint16 mode,
301 const char *buf,
302 size_t size,
303 int i)
305 char *p;
306 bool large_writex = false;
307 /* We can only do direct writes if not signing and not encrypting. */
308 bool direct_writes = !client_is_signing_on(cli) && !cli_encryption_on(cli);
310 if (!direct_writes && size + 1 > cli->bufsize) {
311 cli->outbuf = (char *)SMB_REALLOC(cli->outbuf, size + 1024);
312 if (!cli->outbuf) {
313 return False;
315 cli->inbuf = (char *)SMB_REALLOC(cli->inbuf, size + 1024);
316 if (cli->inbuf == NULL) {
317 SAFE_FREE(cli->outbuf);
318 return False;
320 cli->bufsize = size + 1024;
323 memset(cli->outbuf,'\0',smb_size);
324 memset(cli->inbuf,'\0',smb_size);
326 if (cli->capabilities & CAP_LARGE_FILES) {
327 large_writex = True;
330 if (large_writex) {
331 cli_set_message(cli->outbuf,14,0,True);
332 } else {
333 cli_set_message(cli->outbuf,12,0,True);
336 SCVAL(cli->outbuf,smb_com,SMBwriteX);
337 SSVAL(cli->outbuf,smb_tid,cli->cnum);
338 cli_setup_packet(cli);
340 SCVAL(cli->outbuf,smb_vwv0,0xFF);
341 SSVAL(cli->outbuf,smb_vwv2,fnum);
343 SIVAL(cli->outbuf,smb_vwv3,offset);
344 SIVAL(cli->outbuf,smb_vwv5,0);
345 SSVAL(cli->outbuf,smb_vwv7,mode);
347 SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
349 * According to CIFS-TR-1p00, this following field should only
350 * be set if CAP_LARGE_WRITEX is set. We should check this
351 * locally. However, this check might already have been
352 * done by our callers.
354 SSVAL(cli->outbuf,smb_vwv9,(size>>16));
355 SSVAL(cli->outbuf,smb_vwv10,size);
356 /* +1 is pad byte. */
357 SSVAL(cli->outbuf,smb_vwv11,
358 smb_buf(cli->outbuf) - smb_base(cli->outbuf) + 1);
360 if (large_writex) {
361 SIVAL(cli->outbuf,smb_vwv12,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
364 p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11) -1;
365 *p++ = '\0'; /* pad byte. */
366 if (!direct_writes) {
367 memcpy(p, buf, size);
369 if (size > 0x1FFFF) {
370 /* This is a POSIX 14 word large write. */
371 set_message_bcc(cli->outbuf, 0); /* Set bcc to zero. */
372 _smb_setlen_large(cli->outbuf,smb_size + 28 + 1 /* pad */ + size - 4);
373 } else {
374 cli_setup_bcc(cli, p+size);
377 SSVAL(cli->outbuf,smb_mid,cli->mid + i);
379 show_msg(cli->outbuf);
380 if (direct_writes) {
381 /* For direct writes we now need to write the data
382 * directly out of buf. */
383 return cli_send_smb_direct_writeX(cli, buf, size);
384 } else {
385 return cli_send_smb(cli);
389 /****************************************************************************
390 write to a file
391 write_mode: 0x0001 disallow write cacheing
392 0x0002 return bytes remaining
393 0x0004 use raw named pipe protocol
394 0x0008 start of message mode named pipe protocol
395 ****************************************************************************/
397 ssize_t cli_write(struct cli_state *cli,
398 int fnum, uint16 write_mode,
399 const char *buf, off_t offset, size_t size)
401 ssize_t bwritten = 0;
402 unsigned int issued = 0;
403 unsigned int received = 0;
404 int mpx = 1;
405 size_t writesize;
406 int blocks;
408 if(cli->max_mux > 1) {
409 mpx = cli->max_mux-1;
410 } else {
411 mpx = 1;
414 /* Default (small) writesize. */
415 writesize = (cli->max_xmit - (smb_size+32)) & ~1023;
417 if (write_mode == 0 &&
418 !client_is_signing_on(cli) &&
419 !cli_encryption_on(cli) &&
420 (cli->posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) &&
421 (cli->capabilities & CAP_LARGE_FILES)) {
422 /* Only do massive writes if we can do them direct
423 * with no signing or encrypting - not on a pipe. */
424 writesize = CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE;
425 } else if (cli->capabilities & CAP_LARGE_WRITEX) {
426 if (cli->is_samba) {
427 writesize = CLI_SAMBA_MAX_LARGE_WRITEX_SIZE;
428 } else if (!client_is_signing_on(cli)) {
429 /* Windows restricts signed writes to max_xmit.
430 * Found by Volker. */
431 writesize = CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE;
435 blocks = (size + (writesize-1)) / writesize;
437 while (received < blocks) {
439 while ((issued - received < mpx) && (issued < blocks)) {
440 ssize_t bsent = issued * writesize;
441 ssize_t size1 = MIN(writesize, size - bsent);
443 if (!cli_issue_write(cli, fnum, offset + bsent,
444 write_mode,
445 buf + bsent,
446 size1, issued))
447 return -1;
448 issued++;
451 if (!cli_receive_smb(cli)) {
452 return bwritten;
455 received++;
457 if (cli_is_error(cli))
458 break;
460 bwritten += SVAL(cli->inbuf, smb_vwv2);
461 bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))<<16);
464 while (received < issued && cli_receive_smb(cli)) {
465 received++;
468 return bwritten;
471 /****************************************************************************
472 write to a file using a SMBwrite and not bypassing 0 byte writes
473 ****************************************************************************/
475 ssize_t cli_smbwrite(struct cli_state *cli,
476 int fnum, char *buf, off_t offset, size_t size1)
478 char *p;
479 ssize_t total = 0;
481 do {
482 size_t size = MIN(size1, cli->max_xmit - 48);
484 memset(cli->outbuf,'\0',smb_size);
485 memset(cli->inbuf,'\0',smb_size);
487 cli_set_message(cli->outbuf,5, 0,True);
489 SCVAL(cli->outbuf,smb_com,SMBwrite);
490 SSVAL(cli->outbuf,smb_tid,cli->cnum);
491 cli_setup_packet(cli);
493 SSVAL(cli->outbuf,smb_vwv0,fnum);
494 SSVAL(cli->outbuf,smb_vwv1,size);
495 SIVAL(cli->outbuf,smb_vwv2,offset);
496 SSVAL(cli->outbuf,smb_vwv4,0);
498 p = smb_buf(cli->outbuf);
499 *p++ = 1;
500 SSVAL(p, 0, size); p += 2;
501 memcpy(p, buf + total, size); p += size;
503 cli_setup_bcc(cli, p);
505 if (!cli_send_smb(cli))
506 return -1;
508 if (!cli_receive_smb(cli))
509 return -1;
511 if (cli_is_error(cli))
512 return -1;
514 size = SVAL(cli->inbuf,smb_vwv0);
515 if (size == 0)
516 break;
518 size1 -= size;
519 total += size;
520 offset += size;
522 } while (size1);
524 return total;