Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
[jra/samba/.git] / source3 / libsmb / clireadwrite.c
1 /*
2    Unix SMB/CIFS implementation.
3    client file read/write routines
4    Copyright (C) Andrew Tridgell 1994-1998
5
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.
10
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.
15
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/>.
18 */
19
20 #include "includes.h"
21
22 /****************************************************************************
23 Issue a single SMBread and don't wait for a reply.
24 ****************************************************************************/
25
26 static bool cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
27                            size_t size, int i)
28 {
29         bool bigoffset = False;
30
31         memset(cli->outbuf,'\0',smb_size);
32         memset(cli->inbuf,'\0',smb_size);
33
34         if ((SMB_BIG_UINT)offset >> 32)
35                 bigoffset = True;
36
37         cli_set_message(cli->outbuf,bigoffset ? 12 : 10,0,True);
38
39         SCVAL(cli->outbuf,smb_com,SMBreadX);
40         SSVAL(cli->outbuf,smb_tid,cli->cnum);
41         cli_setup_packet(cli);
42
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);
50
51         if (bigoffset) {
52                 SIVAL(cli->outbuf,smb_vwv10,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
53         }
54
55         return cli_send_smb(cli);
56 }
57
58 /****************************************************************************
59   Read size bytes at offset offset using SMBreadX.
60 ****************************************************************************/
61
62 ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
63 {
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);
70
71         if (size == 0)
72                 return 0;
73
74         /*
75          * Set readsize to the maximum size we can handle in one readX,
76          * rounded down to a multiple of 1024.
77          */
78
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;
88                 }
89         } else {
90                 readsize = (cli->max_xmit - (smb_size+32)) & ~1023;
91         }
92
93         while (total < size) {
94                 readsize = MIN(readsize, size-total);
95
96                 /* Issue a read and receive a reply */
97
98                 if (!cli_issue_read(cli, fnum, offset, readsize, 0))
99                         return -1;
100
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;
107                 }
108
109                 /* Check for error.  Make sure to check for DOS and NT
110                    errors. */
111
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;
117
118                         if (cli_is_nt_error(cli))
119                                 status = cli_nt_error(cli);
120                         else
121                                 cli_dos_error(cli, &eclass, &ecode);
122
123                         /*
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.
127                          */
128
129                         if ((eclass == ERRDOS && ecode == ERRmoredata) ||
130                             NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES))
131                                 recoverable_error = True;
132
133                         if (!recoverable_error)
134                                 return -1;
135                 }
136
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);
141
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;
148                 }
149
150                 if (size2) {
151                         /* smb_vwv6 is the offset in the packet of the returned
152                          * data bytes. Only valid if size2 != 0. */
153
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);
161
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;
167                                 }
168
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;
175                                 }
176                         }
177                 }
178
179                 total += size2;
180                 offset += size2;
181
182                 /*
183                  * If the server returned less than we asked for we're at EOF.
184                  */
185
186                 if (size2 < readsize)
187                         break;
188         }
189
190         return total;
191 }
192
193 #if 0  /* relies on client_receive_smb(), now a static in libsmb/clientgen.c */
194
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 */
198
199 /****************************************************************************
200 Issue a single SMBreadraw and don't wait for a reply.
201 ****************************************************************************/
202
203 static bool cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset, 
204                            size_t size, int i)
205 {
206
207         if (!cli->sign_info.use_smb_signing) {
208                 DEBUG(0, ("Cannot use readraw and SMB Signing\n"));
209                 return False;
210         }
211         
212         memset(cli->outbuf,'\0',smb_size);
213         memset(cli->inbuf,'\0',smb_size);
214
215         cli_set_message(cli->outbuf,10,0,True);
216                 
217         SCVAL(cli->outbuf,smb_com,SMBreadbraw);
218         SSVAL(cli->outbuf,smb_tid,cli->cnum);
219         cli_setup_packet(cli);
220
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);
226
227         return cli_send_smb(cli);
228 }
229
230 /****************************************************************************
231  Tester for the readraw call.
232 ****************************************************************************/
233
234 ssize_t cli_readraw(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
235 {
236         char *p;
237         int size2;
238         size_t readsize;
239         ssize_t total = 0;
240
241         if (size == 0) 
242                 return 0;
243
244         /*
245          * Set readsize to the maximum size we can handle in one readraw.
246          */
247
248         readsize = 0xFFFF;
249
250         while (total < size) {
251                 readsize = MIN(readsize, size-total);
252
253                 /* Issue a read and receive a reply */
254
255                 if (!cli_issue_readraw(cli, fnum, offset, readsize, 0))
256                         return -1;
257
258                 if (!client_receive_smb(cli->fd, cli->inbuf, cli->timeout))
259                         return -1;
260
261                 size2 = smb_len(cli->inbuf);
262
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;
269                 }
270
271                 /* Copy data into buffer */
272
273                 if (size2) {
274                         p = cli->inbuf + 4;
275                         memcpy(buf + total, p, size2);
276                 }
277
278                 total += size2;
279                 offset += size2;
280
281                 /*
282                  * If the server returned less than we asked for we're at EOF.
283                  */
284
285                 if (size2 < readsize)
286                         break;
287         }
288
289         return total;
290 }
291 #endif
292
293 /****************************************************************************
294  Issue a single SMBwrite and don't wait for a reply.
295 ****************************************************************************/
296
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)
304 {
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);
309
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;
314                 }
315                 cli->inbuf = (char *)SMB_REALLOC(cli->inbuf, size + 1024);
316                 if (cli->inbuf == NULL) {
317                         SAFE_FREE(cli->outbuf);
318                         return False;
319                 }
320                 cli->bufsize = size + 1024;
321         }
322
323         memset(cli->outbuf,'\0',smb_size);
324         memset(cli->inbuf,'\0',smb_size);
325
326         if (cli->capabilities & CAP_LARGE_FILES) {
327                 large_writex = True;
328         }
329
330         if (large_writex) {
331                 cli_set_message(cli->outbuf,14,0,True);
332         } else {
333                 cli_set_message(cli->outbuf,12,0,True);
334         }
335
336         SCVAL(cli->outbuf,smb_com,SMBwriteX);
337         SSVAL(cli->outbuf,smb_tid,cli->cnum);
338         cli_setup_packet(cli);
339
340         SCVAL(cli->outbuf,smb_vwv0,0xFF);
341         SSVAL(cli->outbuf,smb_vwv2,fnum);
342
343         SIVAL(cli->outbuf,smb_vwv3,offset);
344         SIVAL(cli->outbuf,smb_vwv5,0);
345         SSVAL(cli->outbuf,smb_vwv7,mode);
346
347         SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
348         /*
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.
353          */
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);
359
360         if (large_writex) {
361                 SIVAL(cli->outbuf,smb_vwv12,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
362         }
363
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);
368         }
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);
375         }
376
377         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
378
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);
386         }
387 }
388
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 ****************************************************************************/
396
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)
400 {
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;
407
408         if(cli->max_mux > 1) {
409                 mpx = cli->max_mux-1;
410         } else {
411                 mpx = 1;
412         }
413
414         /* Default (small) writesize. */
415         writesize = (cli->max_xmit - (smb_size+32)) & ~1023;
416
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;
432                 }
433         }
434
435         blocks = (size + (writesize-1)) / writesize;
436
437         while (received < blocks) {
438
439                 while ((issued - received < mpx) && (issued < blocks)) {
440                         ssize_t bsent = issued * writesize;
441                         ssize_t size1 = MIN(writesize, size - bsent);
442
443                         if (!cli_issue_write(cli, fnum, offset + bsent,
444                                         write_mode,
445                                         buf + bsent,
446                                         size1, issued))
447                                 return -1;
448                         issued++;
449                 }
450
451                 if (!cli_receive_smb(cli)) {
452                         return bwritten;
453                 }
454
455                 received++;
456
457                 if (cli_is_error(cli))
458                         break;
459
460                 bwritten += SVAL(cli->inbuf, smb_vwv2);
461                 bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))<<16);
462         }
463
464         while (received < issued && cli_receive_smb(cli)) {
465                 received++;
466         }
467
468         return bwritten;
469 }
470
471 /****************************************************************************
472   write to a file using a SMBwrite and not bypassing 0 byte writes
473 ****************************************************************************/
474
475 ssize_t cli_smbwrite(struct cli_state *cli,
476                      int fnum, char *buf, off_t offset, size_t size1)
477 {
478         char *p;
479         ssize_t total = 0;
480
481         do {
482                 size_t size = MIN(size1, cli->max_xmit - 48);
483
484                 memset(cli->outbuf,'\0',smb_size);
485                 memset(cli->inbuf,'\0',smb_size);
486
487                 cli_set_message(cli->outbuf,5, 0,True);
488
489                 SCVAL(cli->outbuf,smb_com,SMBwrite);
490                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
491                 cli_setup_packet(cli);
492
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);
497
498                 p = smb_buf(cli->outbuf);
499                 *p++ = 1;
500                 SSVAL(p, 0, size); p += 2;
501                 memcpy(p, buf + total, size); p += size;
502
503                 cli_setup_bcc(cli, p);
504
505                 if (!cli_send_smb(cli))
506                         return -1;
507
508                 if (!cli_receive_smb(cli))
509                         return -1;
510
511                 if (cli_is_error(cli))
512                         return -1;
513
514                 size = SVAL(cli->inbuf,smb_vwv0);
515                 if (size == 0)
516                         break;
517
518                 size1 -= size;
519                 total += size;
520                 offset += size;
521
522         } while (size1);
523
524         return total;
525 }