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