c31d417fbd30f29ce6fdcc5c0e4fcc76eb66239b
[gd/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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /****************************************************************************
24 Issue a single SMBread and don't wait for a reply.
25 ****************************************************************************/
26
27 static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset, 
28                            size_t size, int i)
29 {
30         BOOL bigoffset = False;
31
32         memset(cli->outbuf,'\0',smb_size);
33         memset(cli->inbuf,'\0',smb_size);
34
35         if ((SMB_BIG_UINT)offset >> 32) 
36                 bigoffset = True;
37
38         set_message(NULL,cli->outbuf,bigoffset ? 12 : 10,0,True);
39                 
40         SCVAL(cli->outbuf,smb_com,SMBreadX);
41         SSVAL(cli->outbuf,smb_tid,cli->cnum);
42         cli_setup_packet(cli);
43
44         SCVAL(cli->outbuf,smb_vwv0,0xFF);
45         SSVAL(cli->outbuf,smb_vwv2,fnum);
46         SIVAL(cli->outbuf,smb_vwv3,offset);
47         SSVAL(cli->outbuf,smb_vwv5,size);
48         SSVAL(cli->outbuf,smb_vwv6,size);
49         SSVAL(cli->outbuf,smb_vwv7,(size >> 16));
50         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
51
52         if (bigoffset) {
53                 SIVAL(cli->outbuf,smb_vwv10,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
54         }
55
56         return cli_send_smb(cli);
57 }
58
59 /****************************************************************************
60   Read size bytes at offset offset using SMBreadX.
61 ****************************************************************************/
62
63 ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
64 {
65         char *p;
66         size_t size2;
67         size_t readsize;
68         ssize_t total = 0;
69         /* We can only do direct reads if not signing. */
70         BOOL direct_reads = !client_is_signing_on(cli);
71
72         if (size == 0) 
73                 return 0;
74
75         /*
76          * Set readsize to the maximum size we can handle in one readX,
77          * rounded down to a multiple of 1024.
78          */
79
80         if (client_is_signing_on(cli) == False &&
81                         cli_encryption_on(cli) == False &&
82                         (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
83                 readsize = CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
84         } else if (cli->capabilities & CAP_LARGE_READX) {
85                 if (cli->is_samba) {
86                         readsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
87                 } else {
88                         readsize = CLI_WINDOWS_MAX_LARGE_READX_SIZE;
89                 }
90         } else {
91                 readsize = (cli->max_xmit - (smb_size+32)) & ~1023;
92         }
93
94         while (total < size) {
95                 readsize = MIN(readsize, size-total);
96
97                 /* Issue a read and receive a reply */
98
99                 if (!cli_issue_read(cli, fnum, offset, readsize, 0))
100                         return -1;
101
102                 if (direct_reads) {
103                         if (!cli_receive_smb_readX_header(cli))
104                                 return -1;
105                 } else {
106                         if (!cli_receive_smb(cli))
107                                 return -1;
108                 }
109
110                 /* Check for error.  Make sure to check for DOS and NT
111                    errors. */
112
113                 if (cli_is_error(cli)) {
114                         BOOL recoverable_error = False;
115                         NTSTATUS status = NT_STATUS_OK;
116                         uint8 eclass = 0;
117                         uint32 ecode = 0;
118
119                         if (cli_is_nt_error(cli))
120                                 status = cli_nt_error(cli);
121                         else
122                                 cli_dos_error(cli, &eclass, &ecode);
123
124                         /*
125                          * ERRDOS ERRmoredata or STATUS_MORE_ENRTIES is a
126                          * recoverable error, plus we have valid data in the
127                          * packet so don't error out here.
128                          */
129
130                         if ((eclass == ERRDOS && ecode == ERRmoredata) ||
131                             NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES))
132                                 recoverable_error = True;
133
134                         if (!recoverable_error)
135                                 return -1;
136                 }
137
138                 size2 = SVAL(cli->inbuf, smb_vwv5);
139                 size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7))) << 16);
140
141                 if (size2 > readsize) {
142                         DEBUG(5,("server returned more than we wanted!\n"));
143                         return -1;
144                 } else if (size2 < 0) {
145                         DEBUG(5,("read return < 0!\n"));
146                         return -1;
147                 }
148
149                 if (!direct_reads) {
150                         /* Copy data into buffer */
151                         p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
152                         memcpy(buf + total, p, size2);
153                 } else {
154                         /* Ensure the remaining data matches the return size. */
155                         ssize_t toread = smb_len_large(cli->inbuf) - SVAL(cli->inbuf,smb_vwv6);
156
157                         /* Ensure the size is correct. */
158                         if (toread != size2) {
159                                 DEBUG(5,("direct read logic fail toread (%d) != size2 (%u)\n",
160                                         (int)toread, (unsigned int)size2 ));
161                                 return -1;
162                         }
163
164                         /* Read data directly into buffer */
165                         toread = cli_receive_smb_data(cli,buf+total,size2);
166                         if (toread != size2) {
167                                 DEBUG(5,("direct read read failure toread (%d) != size2 (%u)\n",
168                                         (int)toread, (unsigned int)size2 ));
169                                 return -1;
170                         }
171                 }
172
173                 total += size2;
174                 offset += size2;
175
176                 /*
177                  * If the server returned less than we asked for we're at EOF.
178                  */
179
180                 if (size2 < readsize)
181                         break;
182         }
183
184         return total;
185 }
186
187 #if 0  /* relies on client_receive_smb(), now a static in libsmb/clientgen.c */
188
189 /* This call is INCOMPATIBLE with SMB signing.  If you remove the #if 0
190    you must fix ensure you don't attempt to sign the packets - data
191    *will* be currupted */
192
193 /****************************************************************************
194 Issue a single SMBreadraw and don't wait for a reply.
195 ****************************************************************************/
196
197 static BOOL cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset, 
198                            size_t size, int i)
199 {
200
201         if (!cli->sign_info.use_smb_signing) {
202                 DEBUG(0, ("Cannot use readraw and SMB Signing\n"));
203                 return False;
204         }
205         
206         memset(cli->outbuf,'\0',smb_size);
207         memset(cli->inbuf,'\0',smb_size);
208
209         set_message(NULL,cli->outbuf,10,0,True);
210                 
211         SCVAL(cli->outbuf,smb_com,SMBreadbraw);
212         SSVAL(cli->outbuf,smb_tid,cli->cnum);
213         cli_setup_packet(cli);
214
215         SSVAL(cli->outbuf,smb_vwv0,fnum);
216         SIVAL(cli->outbuf,smb_vwv1,offset);
217         SSVAL(cli->outbuf,smb_vwv2,size);
218         SSVAL(cli->outbuf,smb_vwv3,size);
219         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
220
221         return cli_send_smb(cli);
222 }
223
224 /****************************************************************************
225  Tester for the readraw call.
226 ****************************************************************************/
227
228 ssize_t cli_readraw(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
229 {
230         char *p;
231         int size2;
232         size_t readsize;
233         ssize_t total = 0;
234
235         if (size == 0) 
236                 return 0;
237
238         /*
239          * Set readsize to the maximum size we can handle in one readraw.
240          */
241
242         readsize = 0xFFFF;
243
244         while (total < size) {
245                 readsize = MIN(readsize, size-total);
246
247                 /* Issue a read and receive a reply */
248
249                 if (!cli_issue_readraw(cli, fnum, offset, readsize, 0))
250                         return -1;
251
252                 if (!client_receive_smb(cli->fd, cli->inbuf, cli->timeout))
253                         return -1;
254
255                 size2 = smb_len(cli->inbuf);
256
257                 if (size2 > readsize) {
258                         DEBUG(5,("server returned more than we wanted!\n"));
259                         return -1;
260                 } else if (size2 < 0) {
261                         DEBUG(5,("read return < 0!\n"));
262                         return -1;
263                 }
264
265                 /* Copy data into buffer */
266
267                 if (size2) {
268                         p = cli->inbuf + 4;
269                         memcpy(buf + total, p, size2);
270                 }
271
272                 total += size2;
273                 offset += size2;
274
275                 /*
276                  * If the server returned less than we asked for we're at EOF.
277                  */
278
279                 if (size2 < readsize)
280                         break;
281         }
282
283         return total;
284 }
285 #endif
286 /****************************************************************************
287 issue a single SMBwrite and don't wait for a reply
288 ****************************************************************************/
289
290 static BOOL cli_issue_write(struct cli_state *cli, int fnum, off_t offset, 
291                             uint16 mode, const char *buf,
292                             size_t size, int i)
293 {
294         char *p;
295         BOOL large_writex = False;
296
297         if (size > cli->bufsize) {
298                 cli->outbuf = (char *)SMB_REALLOC(cli->outbuf, size + 1024);
299                 if (!cli->outbuf) {
300                         return False;
301                 }
302                 cli->inbuf = (char *)SMB_REALLOC(cli->inbuf, size + 1024);
303                 if (cli->inbuf == NULL) {
304                         SAFE_FREE(cli->outbuf);
305                         return False;
306                 }
307                 cli->bufsize = size + 1024;
308         }
309
310         memset(cli->outbuf,'\0',smb_size);
311         memset(cli->inbuf,'\0',smb_size);
312
313         if (((SMB_BIG_UINT)offset >> 32) || (size > 0xFFFF)) {
314                 large_writex = True;
315         }
316
317         if (large_writex)
318                 set_message(NULL,cli->outbuf,14,0,True);
319         else
320                 set_message(NULL,cli->outbuf,12,0,True);
321         
322         SCVAL(cli->outbuf,smb_com,SMBwriteX);
323         SSVAL(cli->outbuf,smb_tid,cli->cnum);
324         cli_setup_packet(cli);
325         
326         SCVAL(cli->outbuf,smb_vwv0,0xFF);
327         SSVAL(cli->outbuf,smb_vwv2,fnum);
328
329         SIVAL(cli->outbuf,smb_vwv3,offset);
330         SIVAL(cli->outbuf,smb_vwv5,0);
331         SSVAL(cli->outbuf,smb_vwv7,mode);
332
333         SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
334         /*
335          * According to CIFS-TR-1p00, this following field should only
336          * be set if CAP_LARGE_WRITEX is set. We should check this
337          * locally. However, this check might already have been
338          * done by our callers.
339          */
340         SSVAL(cli->outbuf,smb_vwv9,((size>>16)&1));
341         SSVAL(cli->outbuf,smb_vwv10,size);
342         SSVAL(cli->outbuf,smb_vwv11,
343               smb_buf(cli->outbuf) - smb_base(cli->outbuf));
344
345         if (large_writex) {
346                 SIVAL(cli->outbuf,smb_vwv12,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
347         }
348         
349         p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11);
350         memcpy(p, buf, size);
351         cli_setup_bcc(cli, p+size);
352
353         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
354         
355         show_msg(cli->outbuf);
356         return cli_send_smb(cli);
357 }
358
359 /****************************************************************************
360   write to a file
361   write_mode: 0x0001 disallow write cacheing
362               0x0002 return bytes remaining
363               0x0004 use raw named pipe protocol
364               0x0008 start of message mode named pipe protocol
365 ****************************************************************************/
366
367 ssize_t cli_write(struct cli_state *cli,
368                  int fnum, uint16 write_mode,
369                  const char *buf, off_t offset, size_t size)
370 {
371         ssize_t bwritten = 0;
372         unsigned int issued = 0;
373         unsigned int received = 0;
374         int mpx = 1;
375         int block = cli->max_xmit - (smb_size+32);
376         int blocks = (size + (block-1)) / block;
377
378         if(cli->max_mux > 1) {
379                 mpx = cli->max_mux-1;
380         } else {
381                 mpx = 1;
382         }
383
384         while (received < blocks) {
385
386                 while ((issued - received < mpx) && (issued < blocks)) {
387                         ssize_t bsent = issued * block;
388                         ssize_t size1 = MIN(block, size - bsent);
389
390                         if (!cli_issue_write(cli, fnum, offset + bsent,
391                                         write_mode,
392                                         buf + bsent,
393                                         size1, issued))
394                                 return -1;
395                         issued++;
396                 }
397
398                 if (!cli_receive_smb(cli))
399                         return bwritten;
400
401                 received++;
402
403                 if (cli_is_error(cli))
404                         break;
405
406                 bwritten += SVAL(cli->inbuf, smb_vwv2);
407                 bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))<<16);
408         }
409
410         while (received < issued && cli_receive_smb(cli))
411                 received++;
412         
413         return bwritten;
414 }
415
416 /****************************************************************************
417   write to a file using a SMBwrite and not bypassing 0 byte writes
418 ****************************************************************************/
419
420 ssize_t cli_smbwrite(struct cli_state *cli,
421                      int fnum, char *buf, off_t offset, size_t size1)
422 {
423         char *p;
424         ssize_t total = 0;
425
426         do {
427                 size_t size = MIN(size1, cli->max_xmit - 48);
428                 
429                 memset(cli->outbuf,'\0',smb_size);
430                 memset(cli->inbuf,'\0',smb_size);
431
432                 set_message(NULL,cli->outbuf,5, 0,True);
433
434                 SCVAL(cli->outbuf,smb_com,SMBwrite);
435                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
436                 cli_setup_packet(cli);
437                 
438                 SSVAL(cli->outbuf,smb_vwv0,fnum);
439                 SSVAL(cli->outbuf,smb_vwv1,size);
440                 SIVAL(cli->outbuf,smb_vwv2,offset);
441                 SSVAL(cli->outbuf,smb_vwv4,0);
442                 
443                 p = smb_buf(cli->outbuf);
444                 *p++ = 1;
445                 SSVAL(p, 0, size); p += 2;
446                 memcpy(p, buf + total, size); p += size;
447
448                 cli_setup_bcc(cli, p);
449                 
450                 if (!cli_send_smb(cli))
451                         return -1;
452
453                 if (!cli_receive_smb(cli))
454                         return -1;
455                 
456                 if (cli_is_error(cli))
457                         return -1;
458
459                 size = SVAL(cli->inbuf,smb_vwv0);
460                 if (size == 0)
461                         break;
462
463                 size1 -= size;
464                 total += size;
465                 offset += size;
466
467         } while (size1);
468
469         return total;
470 }