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