added some comments to make the cli read code clearer
[ira/wip.git] / source3 / libsmb / clireadwrite.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    client file read/write routines
5    Copyright (C) Andrew Tridgell 1994-1998
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #define NO_SYSLOG
23
24 #include "includes.h"
25
26 /****************************************************************************
27 issue a single SMBread and don't wait for a reply
28 ****************************************************************************/
29 static void cli_issue_read(struct cli_state *cli, int fnum, off_t offset, 
30                            size_t size, int i)
31 {
32         memset(cli->outbuf,'\0',smb_size);
33         memset(cli->inbuf,'\0',smb_size);
34
35         set_message(cli->outbuf,10,0,True);
36                 
37         CVAL(cli->outbuf,smb_com) = SMBreadX;
38         SSVAL(cli->outbuf,smb_tid,cli->cnum);
39         cli_setup_packet(cli);
40
41         CVAL(cli->outbuf,smb_vwv0) = 0xFF;
42         SSVAL(cli->outbuf,smb_vwv2,fnum);
43         SIVAL(cli->outbuf,smb_vwv3,offset);
44         SSVAL(cli->outbuf,smb_vwv5,size);
45         SSVAL(cli->outbuf,smb_vwv6,size);
46         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
47
48         cli_send_smb(cli);
49 }
50
51 /****************************************************************************
52   read from a file
53 ****************************************************************************/
54 size_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
55 {
56         char *p;
57         int total = -1;
58 /*
59  * There is a problem in this code when mpx is more than one.
60  * for some reason files can get corrupted when being read.
61  * Until we understand this fully I am serializing reads (one
62  * read/one reply) for now. JRA.
63  */
64 #if 0
65         int mpx = MAX(cli->max_mux-1, 1); 
66 #else
67         int mpx = 1;
68 #endif
69         int block;
70         int mid;
71         int blocks;
72         /* issued is the number of readX requests we have sent so far */
73         int issued=0;
74         /* received is the number of readX replies we have received */
75         int received=0;
76
77         /* maybe its a very silly request? */
78         if (size == 0) return 0;
79
80         /* set block to the maximum size we can handle in one readX,
81            rounded down to a multiple of 1024 */
82         block = (cli->max_xmit - (smb_size+32)) & ~1023;
83
84         /* work out how many readX calls we will need in total */
85         blocks = (size + (block-1)) / block;
86
87         while (received < blocks) {
88                 int size2;
89
90                 while (issued - received < mpx && issued < blocks) {
91                         int size1 = MIN(block, size-issued*block);
92                         cli_issue_read(cli, fnum, offset+issued*block, size1, issued);
93                         issued++;
94                 }
95
96                 if (!cli_receive_smb(cli)) {
97                         return total;
98                 }
99
100                 received++;
101                 mid = SVAL(cli->inbuf, smb_mid) - cli->mid;
102                 size2 = SVAL(cli->inbuf, smb_vwv5);
103
104                 if (CVAL(cli->inbuf,smb_rcls) != 0) {
105                         blocks = MIN(blocks, mid-1);
106                         continue;
107                 }
108
109                 if (size2 <= 0) {
110                         blocks = MIN(blocks, mid-1);
111                         /* this distinguishes EOF from an error */
112                         total = MAX(total, 0);
113                         continue;
114                 }
115
116                 if (size2 > block) {
117                         DEBUG(0,("server returned more than we wanted!\n"));
118                         return -1;
119                 }
120                 if (mid >= issued) {
121                         DEBUG(0,("invalid mid from server!\n"));
122                         return -1;
123                 }
124                 p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
125
126                 memcpy(buf+mid*block, p, size2);
127
128                 total = MAX(total, mid*block + size2);
129         }
130
131         while (received < issued) {
132                 cli_receive_smb(cli);
133                 received++;
134         }
135         
136         return total;
137 }
138
139
140 /****************************************************************************
141 issue a single SMBwrite and don't wait for a reply
142 ****************************************************************************/
143 static void cli_issue_write(struct cli_state *cli, int fnum, off_t offset, uint16 mode, char *buf,
144                             size_t size, int i)
145 {
146         char *p;
147
148         memset(cli->outbuf,'\0',smb_size);
149         memset(cli->inbuf,'\0',smb_size);
150
151         if (size > 0xFFFF)
152                 set_message(cli->outbuf,14,0,True);
153         else
154                 set_message(cli->outbuf,12,0,True);
155         
156         CVAL(cli->outbuf,smb_com) = SMBwriteX;
157         SSVAL(cli->outbuf,smb_tid,cli->cnum);
158         cli_setup_packet(cli);
159         
160         CVAL(cli->outbuf,smb_vwv0) = 0xFF;
161         SSVAL(cli->outbuf,smb_vwv2,fnum);
162
163         SIVAL(cli->outbuf,smb_vwv3,offset);
164         SIVAL(cli->outbuf,smb_vwv5,(mode & 0x0008) ? 0xFFFFFFFF : 0);
165         SSVAL(cli->outbuf,smb_vwv7,mode);
166
167         SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
168         SSVAL(cli->outbuf,smb_vwv9,((size>>16)&1));
169         SSVAL(cli->outbuf,smb_vwv10,size);
170         SSVAL(cli->outbuf,smb_vwv11,
171               smb_buf(cli->outbuf) - smb_base(cli->outbuf));
172         
173         p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11);
174         memcpy(p, buf, size);
175         cli_setup_bcc(cli, p+size);
176
177         SSVAL(cli->outbuf,smb_mid,cli->mid + i);
178         
179         show_msg(cli->outbuf);
180         cli_send_smb(cli);
181 }
182
183 /****************************************************************************
184   write to a file
185   write_mode: 0x0001 disallow write cacheing
186               0x0002 return bytes remaining
187               0x0004 use raw named pipe protocol
188               0x0008 start of message mode named pipe protocol
189 ****************************************************************************/
190 ssize_t cli_write(struct cli_state *cli,
191                   int fnum, uint16 write_mode,
192                   char *buf, off_t offset, size_t size)
193 {
194         int bwritten = 0;
195         int issued = 0;
196         int received = 0;
197         int mpx = MAX(cli->max_mux-1, 1);
198         int block = (cli->max_xmit - (smb_size+32)) & ~1023;
199         int blocks = (size + (block-1)) / block;
200
201         while (received < blocks) {
202
203                 while ((issued - received < mpx) && (issued < blocks))
204                 {
205                         int bsent = issued * block;
206                         int size1 = MIN(block, size - bsent);
207
208                         cli_issue_write(cli, fnum, offset + bsent,
209                                         write_mode,
210                                         buf + bsent,
211                                         size1, issued);
212                         issued++;
213                 }
214
215                 if (!cli_receive_smb(cli))
216                 {
217                         return bwritten;
218                 }
219
220                 received++;
221
222                 if (CVAL(cli->inbuf,smb_rcls) != 0)
223                 {
224                         break;
225                 }
226
227                 bwritten += SVAL(cli->inbuf, smb_vwv2);
228         }
229
230         while (received < issued && cli_receive_smb(cli))
231         {
232                 received++;
233         }
234         
235         return bwritten;
236 }
237
238
239 /****************************************************************************
240   write to a file using a SMBwrite and not bypassing 0 byte writes
241 ****************************************************************************/
242 ssize_t cli_smbwrite(struct cli_state *cli,
243                      int fnum, char *buf, off_t offset, size_t size1)
244 {
245         char *p;
246         ssize_t total = 0;
247
248         do {
249                 size_t size = MIN(size1, cli->max_xmit - 48);
250                 
251                 memset(cli->outbuf,'\0',smb_size);
252                 memset(cli->inbuf,'\0',smb_size);
253
254                 set_message(cli->outbuf,5, 0,True);
255
256                 CVAL(cli->outbuf,smb_com) = SMBwrite;
257                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
258                 cli_setup_packet(cli);
259                 
260                 SSVAL(cli->outbuf,smb_vwv0,fnum);
261                 SSVAL(cli->outbuf,smb_vwv1,size);
262                 SIVAL(cli->outbuf,smb_vwv2,offset);
263                 SSVAL(cli->outbuf,smb_vwv4,0);
264                 
265                 p = smb_buf(cli->outbuf);
266                 *p++ = 1;
267                 SSVAL(p, 0, size); p += 2;
268                 memcpy(p, buf, size); p += size;
269
270                 cli_setup_bcc(cli, p);
271                 
272                 cli_send_smb(cli);
273                 if (!cli_receive_smb(cli)) {
274                         return -1;
275                 }
276                 
277                 if (CVAL(cli->inbuf,smb_rcls) != 0) {
278                         return -1;
279                 }
280
281                 size = SVAL(cli->inbuf,smb_vwv0);
282                 if (size == 0) break;
283
284                 size1 -= size;
285                 total += size;
286         } while (size1);
287
288         return total;
289 }
290