1/* 2 Unix SMB/CIFS implementation. 3 4 POSIX NTVFS backend - setfileinfo 5 6 Copyright (C) Andrew Tridgell 2004 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#include "includes.h" 23#include "vfs_posix.h" 24#include "system/time.h" 25#include "librpc/gen_ndr/xattr.h" 26 27 28/* 29 determine what access bits are needed for a call 30*/ 31static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info) 32{ 33 uint32_t needed; 34 35 switch (info->generic.level) { 36 case RAW_SFILEINFO_EA_SET: 37 needed = SEC_FILE_WRITE_EA; 38 break; 39 40 case RAW_SFILEINFO_DISPOSITION_INFO: 41 case RAW_SFILEINFO_DISPOSITION_INFORMATION: 42 needed = SEC_STD_DELETE; 43 break; 44 45 case RAW_SFILEINFO_END_OF_FILE_INFO: 46 needed = SEC_FILE_WRITE_DATA; 47 break; 48 49 case RAW_SFILEINFO_POSITION_INFORMATION: 50 needed = 0; 51 break; 52 53 case RAW_SFILEINFO_SEC_DESC: 54 needed = 0; 55 if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) { 56 needed |= SEC_STD_WRITE_OWNER; 57 } 58 if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) { 59 needed |= SEC_STD_WRITE_DAC; 60 } 61 if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) { 62 needed |= SEC_FLAG_SYSTEM_SECURITY; 63 } 64 break; 65 66 case RAW_SFILEINFO_RENAME_INFORMATION: 67 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: 68 needed = SEC_STD_DELETE; 69 break; 70 71 default: 72 needed = SEC_FILE_WRITE_ATTRIBUTE; 73 break; 74 } 75 76 return needed; 77} 78 79/* 80 rename_information level for streams 81*/ 82static NTSTATUS pvfs_setfileinfo_rename_stream(struct pvfs_state *pvfs, 83 struct ntvfs_request *req, 84 struct pvfs_filename *name, 85 int fd, 86 DATA_BLOB *odb_locking_key, 87 union smb_setfileinfo *info) 88{ 89 NTSTATUS status; 90 struct odb_lock *lck = NULL; 91 92 if (info->rename_information.in.new_name[0] != ':') { 93 return NT_STATUS_INVALID_PARAMETER; 94 } 95 96 status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE); 97 if (!NT_STATUS_IS_OK(status)) { 98 return status; 99 } 100 101 lck = odb_lock(req, pvfs->odb_context, odb_locking_key); 102 if (lck == NULL) { 103 DEBUG(0,("Unable to lock opendb for can_stat\n")); 104 return NT_STATUS_INTERNAL_DB_CORRUPTION; 105 } 106 107 108 status = pvfs_stream_rename(pvfs, name, fd, 109 info->rename_information.in.new_name+1); 110 return status; 111} 112 113/* 114 rename_information level 115*/ 116static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, 117 struct ntvfs_request *req, 118 struct pvfs_filename *name, 119 int fd, 120 DATA_BLOB *odb_locking_key, 121 union smb_setfileinfo *info) 122{ 123 NTSTATUS status; 124 struct pvfs_filename *name2; 125 char *new_name, *p; 126 struct odb_lock *lck = NULL; 127 128 /* renames are only allowed within a directory */ 129 if (strchr_m(info->rename_information.in.new_name, '\\') && 130 (req->ctx->protocol != PROTOCOL_SMB2)) { 131 return NT_STATUS_NOT_SUPPORTED; 132 } 133 134 /* handle stream renames specially */ 135 if (name->stream_name) { 136 return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd, 137 odb_locking_key, info); 138 } 139 140 /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes, 141 but I suspect it is just uninitialised memory */ 142 if (info->rename_information.in.root_fid != 0 && 143 (req->ctx->protocol != PROTOCOL_SMB2)) { 144 return NT_STATUS_INVALID_PARAMETER; 145 } 146 147 /* construct the fully qualified windows name for the new file name */ 148 if (req->ctx->protocol == PROTOCOL_SMB2) { 149 /* SMB2 sends the full path of the new name */ 150 new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name); 151 } else { 152 new_name = talloc_strdup(req, name->original_name); 153 if (new_name == NULL) { 154 return NT_STATUS_NO_MEMORY; 155 } 156 p = strrchr_m(new_name, '\\'); 157 if (p == NULL) { 158 return NT_STATUS_OBJECT_NAME_INVALID; 159 } else { 160 *p = 0; 161 } 162 163 new_name = talloc_asprintf(req, "%s\\%s", new_name, 164 info->rename_information.in.new_name); 165 } 166 if (new_name == NULL) { 167 return NT_STATUS_NO_MEMORY; 168 } 169 170 /* resolve the new name */ 171 status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2); 172 if (!NT_STATUS_IS_OK(status)) { 173 return status; 174 } 175 176 /* if the destination exists, then check the rename is allowed */ 177 if (name2->exists) { 178 if (strcmp(name2->full_name, name->full_name) == 0) { 179 /* rename to same name is null-op */ 180 return NT_STATUS_OK; 181 } 182 183 if (!info->rename_information.in.overwrite) { 184 return NT_STATUS_OBJECT_NAME_COLLISION; 185 } 186 187 status = pvfs_can_delete(pvfs, req, name2, NULL); 188 if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { 189 return NT_STATUS_ACCESS_DENIED; 190 } 191 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { 192 return NT_STATUS_ACCESS_DENIED; 193 } 194 if (!NT_STATUS_IS_OK(status)) { 195 return status; 196 } 197 } 198 199 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE); 200 if (!NT_STATUS_IS_OK(status)) { 201 return status; 202 } 203 204 lck = odb_lock(req, pvfs->odb_context, odb_locking_key); 205 if (lck == NULL) { 206 DEBUG(0,("Unable to lock opendb for can_stat\n")); 207 return NT_STATUS_INTERNAL_DB_CORRUPTION; 208 } 209 210 status = pvfs_do_rename(pvfs, lck, name, name2->full_name); 211 talloc_free(lck); 212 NT_STATUS_NOT_OK_RETURN(status); 213 if (NT_STATUS_IS_OK(status)) { 214 name->full_name = talloc_steal(name, name2->full_name); 215 name->original_name = talloc_steal(name, name2->original_name); 216 } 217 218 return NT_STATUS_OK; 219} 220 221/* 222 add a single DOS EA 223*/ 224NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs, 225 struct pvfs_filename *name, 226 int fd, uint16_t num_eas, 227 struct ea_struct *eas) 228{ 229 struct xattr_DosEAs *ealist; 230 int i, j; 231 NTSTATUS status; 232 233 if (num_eas == 0) { 234 return NT_STATUS_OK; 235 } 236 237 if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { 238 return NT_STATUS_NOT_SUPPORTED; 239 } 240 241 ealist = talloc(name, struct xattr_DosEAs); 242 243 /* load the current list */ 244 status = pvfs_doseas_load(pvfs, name, fd, ealist); 245 if (!NT_STATUS_IS_OK(status)) { 246 return status; 247 } 248 249 for (j=0;j<num_eas;j++) { 250 struct ea_struct *ea = &eas[j]; 251 /* see if its already there */ 252 for (i=0;i<ealist->num_eas;i++) { 253 if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) { 254 ealist->eas[i].value = ea->value; 255 break; 256 } 257 } 258 259 if (i==ealist->num_eas) { 260 /* add it */ 261 ealist->eas = talloc_realloc(ealist, ealist->eas, 262 struct xattr_EA, 263 ealist->num_eas+1); 264 if (ealist->eas == NULL) { 265 return NT_STATUS_NO_MEMORY; 266 } 267 ealist->eas[i].name = ea->name.s; 268 ealist->eas[i].value = ea->value; 269 ealist->num_eas++; 270 } 271 } 272 273 /* pull out any null EAs */ 274 for (i=0;i<ealist->num_eas;i++) { 275 if (ealist->eas[i].value.length == 0) { 276 memmove(&ealist->eas[i], 277 &ealist->eas[i+1], 278 (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i])); 279 ealist->num_eas--; 280 i--; 281 } 282 } 283 284 status = pvfs_doseas_save(pvfs, name, fd, ealist); 285 if (!NT_STATUS_IS_OK(status)) { 286 return status; 287 } 288 289 notify_trigger(pvfs->notify_context, 290 NOTIFY_ACTION_MODIFIED, 291 FILE_NOTIFY_CHANGE_EA, 292 name->full_name); 293 294 name->dos.ea_size = 4; 295 for (i=0;i<ealist->num_eas;i++) { 296 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 + 297 ealist->eas[i].value.length; 298 } 299 300 /* update the ea_size attrib */ 301 return pvfs_dosattrib_save(pvfs, name, fd); 302} 303 304/* 305 set info on a open file 306*/ 307NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, 308 struct ntvfs_request *req, 309 union smb_setfileinfo *info) 310{ 311 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, 312 struct pvfs_state); 313 struct pvfs_file *f; 314 struct pvfs_file_handle *h; 315 struct pvfs_filename newstats; 316 NTSTATUS status; 317 uint32_t access_needed; 318 uint32_t change_mask = 0; 319 320 f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs); 321 if (!f) { 322 return NT_STATUS_INVALID_HANDLE; 323 } 324 325 h = f->handle; 326 327 access_needed = pvfs_setfileinfo_access(info); 328 if ((f->access_mask & access_needed) != access_needed) { 329 return NT_STATUS_ACCESS_DENIED; 330 } 331 332 /* update the file information */ 333 status = pvfs_resolve_name_handle(pvfs, h); 334 if (!NT_STATUS_IS_OK(status)) { 335 return status; 336 } 337 338 /* we take a copy of the current file stats, then update 339 newstats in each of the elements below. At the end we 340 compare, and make any changes needed */ 341 newstats = *h->name; 342 343 switch (info->generic.level) { 344 case RAW_SFILEINFO_SETATTR: 345 if (!null_time(info->setattr.in.write_time)) { 346 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time); 347 } 348 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) { 349 newstats.dos.attrib = info->setattr.in.attrib; 350 } 351 break; 352 353 case RAW_SFILEINFO_SETATTRE: 354 case RAW_SFILEINFO_STANDARD: 355 if (!null_time(info->setattre.in.create_time)) { 356 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time); 357 } 358 if (!null_time(info->setattre.in.access_time)) { 359 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time); 360 } 361 if (!null_time(info->setattre.in.write_time)) { 362 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time); 363 } 364 break; 365 366 case RAW_SFILEINFO_EA_SET: 367 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd, 368 info->ea_set.in.num_eas, 369 info->ea_set.in.eas); 370 371 case RAW_SFILEINFO_BASIC_INFO: 372 case RAW_SFILEINFO_BASIC_INFORMATION: 373 if (!null_nttime(info->basic_info.in.create_time)) { 374 newstats.dos.create_time = info->basic_info.in.create_time; 375 } 376 if (!null_nttime(info->basic_info.in.access_time)) { 377 newstats.dos.access_time = info->basic_info.in.access_time; 378 } 379 if (!null_nttime(info->basic_info.in.write_time)) { 380 newstats.dos.write_time = info->basic_info.in.write_time; 381 } 382 if (!null_nttime(info->basic_info.in.change_time)) { 383 newstats.dos.change_time = info->basic_info.in.change_time; 384 } 385 if (info->basic_info.in.attrib != 0) { 386 newstats.dos.attrib = info->basic_info.in.attrib; 387 } 388 break; 389 390 case RAW_SFILEINFO_DISPOSITION_INFO: 391 case RAW_SFILEINFO_DISPOSITION_INFORMATION: 392 return pvfs_set_delete_on_close(pvfs, req, f, 393 info->disposition_info.in.delete_on_close); 394 395 case RAW_SFILEINFO_ALLOCATION_INFO: 396 case RAW_SFILEINFO_ALLOCATION_INFORMATION: 397 status = pvfs_break_level2_oplocks(f); 398 NT_STATUS_NOT_OK_RETURN(status); 399 400 newstats.dos.alloc_size = info->allocation_info.in.alloc_size; 401 if (newstats.dos.alloc_size < newstats.st.st_size) { 402 newstats.st.st_size = newstats.dos.alloc_size; 403 } 404 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 405 newstats.dos.alloc_size); 406 break; 407 408 case RAW_SFILEINFO_END_OF_FILE_INFO: 409 case RAW_SFILEINFO_END_OF_FILE_INFORMATION: 410 status = pvfs_break_level2_oplocks(f); 411 NT_STATUS_NOT_OK_RETURN(status); 412 413 newstats.st.st_size = info->end_of_file_info.in.size; 414 break; 415 416 case RAW_SFILEINFO_POSITION_INFORMATION: 417 h->position = info->position_information.in.position; 418 break; 419 420 case RAW_SFILEINFO_MODE_INFORMATION: 421 /* this one is a puzzle */ 422 if (info->mode_information.in.mode != 0 && 423 info->mode_information.in.mode != 2 && 424 info->mode_information.in.mode != 4 && 425 info->mode_information.in.mode != 6) { 426 return NT_STATUS_INVALID_PARAMETER; 427 } 428 h->mode = info->mode_information.in.mode; 429 break; 430 431 case RAW_SFILEINFO_RENAME_INFORMATION: 432 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: 433 return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd, 434 &h->odb_locking_key, 435 info); 436 437 case RAW_SFILEINFO_SEC_DESC: 438 notify_trigger(pvfs->notify_context, 439 NOTIFY_ACTION_MODIFIED, 440 FILE_NOTIFY_CHANGE_SECURITY, 441 h->name->full_name); 442 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info); 443 444 default: 445 return NT_STATUS_INVALID_LEVEL; 446 } 447 448 /* possibly change the file size */ 449 if (newstats.st.st_size != h->name->st.st_size) { 450 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) { 451 return NT_STATUS_FILE_IS_A_DIRECTORY; 452 } 453 if (h->name->stream_name) { 454 status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size); 455 if (!NT_STATUS_IS_OK(status)) { 456 return status; 457 } 458 459 change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE; 460 } else { 461 int ret; 462 if (f->access_mask & 463 (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) { 464 ret = ftruncate(h->fd, newstats.st.st_size); 465 } else { 466 ret = truncate(h->name->full_name, newstats.st.st_size); 467 } 468 if (ret == -1) { 469 return pvfs_map_errno(pvfs, errno); 470 } 471 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES; 472 } 473 } 474 475 /* possibly change the file timestamps */ 476 if (newstats.dos.create_time != h->name->dos.create_time) { 477 change_mask |= FILE_NOTIFY_CHANGE_CREATION; 478 } 479 if (newstats.dos.access_time != h->name->dos.access_time) { 480 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 481 } 482 if (newstats.dos.write_time != h->name->dos.write_time) { 483 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE; 484 } 485 if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) || 486 (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) { 487 struct timeval tv[2]; 488 489 nttime_to_timeval(&tv[0], newstats.dos.access_time); 490 nttime_to_timeval(&tv[1], newstats.dos.write_time); 491 492 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) { 493 if (utimes(h->name->full_name, tv) == -1) { 494 DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n", 495 h->name->full_name, strerror(errno))); 496 return pvfs_map_errno(pvfs, errno); 497 } 498 } 499 } 500 if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) { 501 struct odb_lock *lck; 502 503 lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key); 504 if (lck == NULL) { 505 DEBUG(0,("Unable to lock opendb for write time update\n")); 506 return NT_STATUS_INTERNAL_ERROR; 507 } 508 509 status = odb_set_write_time(lck, newstats.dos.write_time, true); 510 if (!NT_STATUS_IS_OK(status)) { 511 DEBUG(0,("Unable to update write time: %s\n", 512 nt_errstr(status))); 513 talloc_free(lck); 514 return status; 515 } 516 517 talloc_free(lck); 518 519 h->write_time.update_forced = true; 520 h->write_time.update_on_close = false; 521 talloc_free(h->write_time.update_event); 522 h->write_time.update_event = NULL; 523 } 524 525 /* possibly change the attribute */ 526 if (newstats.dos.attrib != h->name->dos.attrib) { 527 mode_t mode; 528 if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) && 529 !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { 530 return NT_STATUS_INVALID_PARAMETER; 531 } 532 mode = pvfs_fileperms(pvfs, newstats.dos.attrib); 533 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { 534 if (fchmod(h->fd, mode) == -1) { 535 return pvfs_map_errno(pvfs, errno); 536 } 537 } 538 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 539 } 540 541 *h->name = newstats; 542 543 notify_trigger(pvfs->notify_context, 544 NOTIFY_ACTION_MODIFIED, 545 change_mask, 546 h->name->full_name); 547 548 return pvfs_dosattrib_save(pvfs, h->name, h->fd); 549} 550 551/* 552 retry an open after a sharing violation 553*/ 554static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r, 555 struct ntvfs_module_context *ntvfs, 556 struct ntvfs_request *req, 557 void *_info, 558 void *private_data, 559 enum pvfs_wait_notice reason) 560{ 561 union smb_setfileinfo *info = talloc_get_type(_info, 562 union smb_setfileinfo); 563 NTSTATUS status = NT_STATUS_INTERNAL_ERROR; 564 565 talloc_free(r); 566 567 switch (reason) { 568 case PVFS_WAIT_CANCEL: 569/*TODO*/ 570 status = NT_STATUS_CANCELLED; 571 break; 572 case PVFS_WAIT_TIMEOUT: 573 /* if it timed out, then give the failure 574 immediately */ 575/*TODO*/ 576 status = NT_STATUS_SHARING_VIOLATION; 577 break; 578 case PVFS_WAIT_EVENT: 579 580 /* try the open again, which could trigger another retry setup 581 if it wants to, so we have to unmark the async flag so we 582 will know if it does a second async reply */ 583 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC; 584 585 status = pvfs_setpathinfo(ntvfs, req, info); 586 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { 587 /* the 2nd try also replied async, so we don't send 588 the reply yet */ 589 return; 590 } 591 592 /* re-mark it async, just in case someone up the chain does 593 paranoid checking */ 594 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; 595 break; 596 } 597 598 /* send the reply up the chain */ 599 req->async_states->status = status; 600 req->async_states->send_fn(req); 601} 602 603/* 604 setup for a unlink retry after a sharing violation 605 or a non granted oplock 606*/ 607static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs, 608 struct ntvfs_request *req, 609 union smb_setfileinfo *info, 610 struct odb_lock *lck, 611 NTSTATUS status) 612{ 613 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, 614 struct pvfs_state); 615 struct timeval end_time; 616 617 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { 618 end_time = timeval_add(&req->statistics.request_time, 619 0, pvfs->sharing_violation_delay); 620 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) { 621 end_time = timeval_add(&req->statistics.request_time, 622 pvfs->oplock_break_timeout, 0); 623 } else { 624 return NT_STATUS_INTERNAL_ERROR; 625 } 626 627 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL, 628 pvfs_retry_setpathinfo); 629} 630 631/* 632 set info on a pathname 633*/ 634NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, 635 struct ntvfs_request *req, union smb_setfileinfo *info) 636{ 637 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, 638 struct pvfs_state); 639 struct pvfs_filename *name; 640 struct pvfs_filename newstats; 641 NTSTATUS status; 642 uint32_t access_needed; 643 uint32_t change_mask = 0; 644 struct odb_lock *lck = NULL; 645 DATA_BLOB odb_locking_key; 646 647 /* resolve the cifs name to a posix name */ 648 status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, 649 PVFS_RESOLVE_STREAMS, &name); 650 if (!NT_STATUS_IS_OK(status)) { 651 return status; 652 } 653 654 if (!name->exists) { 655 return NT_STATUS_OBJECT_NAME_NOT_FOUND; 656 } 657 658 access_needed = pvfs_setfileinfo_access(info); 659 status = pvfs_access_check_simple(pvfs, req, name, access_needed); 660 if (!NT_STATUS_IS_OK(status)) { 661 return status; 662 } 663 664 /* we take a copy of the current file stats, then update 665 newstats in each of the elements below. At the end we 666 compare, and make any changes needed */ 667 newstats = *name; 668 669 switch (info->generic.level) { 670 case RAW_SFILEINFO_SETATTR: 671 if (!null_time(info->setattr.in.write_time)) { 672 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time); 673 } 674 if (info->setattr.in.attrib == 0) { 675 newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL; 676 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) { 677 newstats.dos.attrib = info->setattr.in.attrib; 678 } 679 break; 680 681 case RAW_SFILEINFO_SETATTRE: 682 case RAW_SFILEINFO_STANDARD: 683 if (!null_time(info->setattre.in.create_time)) { 684 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time); 685 } 686 if (!null_time(info->setattre.in.access_time)) { 687 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time); 688 } 689 if (!null_time(info->setattre.in.write_time)) { 690 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time); 691 } 692 break; 693 694 case RAW_SFILEINFO_EA_SET: 695 return pvfs_setfileinfo_ea_set(pvfs, name, -1, 696 info->ea_set.in.num_eas, 697 info->ea_set.in.eas); 698 699 case RAW_SFILEINFO_BASIC_INFO: 700 case RAW_SFILEINFO_BASIC_INFORMATION: 701 if (!null_nttime(info->basic_info.in.create_time)) { 702 newstats.dos.create_time = info->basic_info.in.create_time; 703 } 704 if (!null_nttime(info->basic_info.in.access_time)) { 705 newstats.dos.access_time = info->basic_info.in.access_time; 706 } 707 if (!null_nttime(info->basic_info.in.write_time)) { 708 newstats.dos.write_time = info->basic_info.in.write_time; 709 } 710 if (!null_nttime(info->basic_info.in.change_time)) { 711 newstats.dos.change_time = info->basic_info.in.change_time; 712 } 713 if (info->basic_info.in.attrib != 0) { 714 newstats.dos.attrib = info->basic_info.in.attrib; 715 } 716 break; 717 718 case RAW_SFILEINFO_ALLOCATION_INFO: 719 case RAW_SFILEINFO_ALLOCATION_INFORMATION: 720 status = pvfs_can_update_file_size(pvfs, req, name, &lck); 721 /* 722 * on a sharing violation we need to retry when the file is closed by 723 * the other user, or after 1 second 724 * on a non granted oplock we need to retry when the file is closed by 725 * the other user, or after 30 seconds 726 */ 727 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) || 728 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) && 729 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 730 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status); 731 } 732 NT_STATUS_NOT_OK_RETURN(status); 733 734 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) { 735 /* strange. Increasing the allocation size via setpathinfo 736 should be silently ignored */ 737 break; 738 } 739 newstats.dos.alloc_size = info->allocation_info.in.alloc_size; 740 if (newstats.dos.alloc_size < newstats.st.st_size) { 741 newstats.st.st_size = newstats.dos.alloc_size; 742 } 743 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 744 newstats.dos.alloc_size); 745 break; 746 747 case RAW_SFILEINFO_END_OF_FILE_INFO: 748 case RAW_SFILEINFO_END_OF_FILE_INFORMATION: 749 status = pvfs_can_update_file_size(pvfs, req, name, &lck); 750 /* 751 * on a sharing violation we need to retry when the file is closed by 752 * the other user, or after 1 second 753 * on a non granted oplock we need to retry when the file is closed by 754 * the other user, or after 30 seconds 755 */ 756 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) || 757 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) && 758 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 759 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status); 760 } 761 NT_STATUS_NOT_OK_RETURN(status); 762 763 newstats.st.st_size = info->end_of_file_info.in.size; 764 break; 765 766 case RAW_SFILEINFO_MODE_INFORMATION: 767 if (info->mode_information.in.mode != 0 && 768 info->mode_information.in.mode != 2 && 769 info->mode_information.in.mode != 4 && 770 info->mode_information.in.mode != 6) { 771 return NT_STATUS_INVALID_PARAMETER; 772 } 773 return NT_STATUS_OK; 774 775 case RAW_SFILEINFO_RENAME_INFORMATION: 776 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: 777 status = pvfs_locking_key(name, name, &odb_locking_key); 778 NT_STATUS_NOT_OK_RETURN(status); 779 status = pvfs_setfileinfo_rename(pvfs, req, name, -1, 780 &odb_locking_key, info); 781 NT_STATUS_NOT_OK_RETURN(status); 782 return NT_STATUS_OK; 783 784 case RAW_SFILEINFO_DISPOSITION_INFO: 785 case RAW_SFILEINFO_DISPOSITION_INFORMATION: 786 case RAW_SFILEINFO_POSITION_INFORMATION: 787 return NT_STATUS_OK; 788 789 default: 790 return NT_STATUS_INVALID_LEVEL; 791 } 792 793 /* possibly change the file size */ 794 if (newstats.st.st_size != name->st.st_size) { 795 if (name->stream_name) { 796 status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size); 797 if (!NT_STATUS_IS_OK(status)) { 798 return status; 799 } 800 } else if (truncate(name->full_name, newstats.st.st_size) == -1) { 801 return pvfs_map_errno(pvfs, errno); 802 } 803 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES; 804 } 805 806 /* possibly change the file timestamps */ 807 if (newstats.dos.create_time != name->dos.create_time) { 808 change_mask |= FILE_NOTIFY_CHANGE_CREATION; 809 } 810 if (newstats.dos.access_time != name->dos.access_time) { 811 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 812 } 813 if (newstats.dos.write_time != name->dos.write_time) { 814 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE; 815 } 816 if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) || 817 (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) { 818 struct timeval tv[2]; 819 820 nttime_to_timeval(&tv[0], newstats.dos.access_time); 821 nttime_to_timeval(&tv[1], newstats.dos.write_time); 822 823 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) { 824 if (utimes(name->full_name, tv) == -1) { 825 DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n", 826 name->full_name, strerror(errno))); 827 return pvfs_map_errno(pvfs, errno); 828 } 829 } 830 } 831 if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) { 832 if (lck == NULL) { 833 DATA_BLOB lkey; 834 status = pvfs_locking_key(name, name, &lkey); 835 NT_STATUS_NOT_OK_RETURN(status); 836 837 lck = odb_lock(req, pvfs->odb_context, &lkey); 838 data_blob_free(&lkey); 839 if (lck == NULL) { 840 DEBUG(0,("Unable to lock opendb for write time update\n")); 841 return NT_STATUS_INTERNAL_ERROR; 842 } 843 } 844 845 status = odb_set_write_time(lck, newstats.dos.write_time, true); 846 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { 847 /* it could be that nobody has opened the file */ 848 } else if (!NT_STATUS_IS_OK(status)) { 849 DEBUG(0,("Unable to update write time: %s\n", 850 nt_errstr(status))); 851 return status; 852 } 853 } 854 855 /* possibly change the attribute */ 856 newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY); 857 if (newstats.dos.attrib != name->dos.attrib) { 858 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib); 859 if (chmod(name->full_name, mode) == -1) { 860 return pvfs_map_errno(pvfs, errno); 861 } 862 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 863 } 864 865 *name = newstats; 866 867 if (change_mask != 0) { 868 notify_trigger(pvfs->notify_context, 869 NOTIFY_ACTION_MODIFIED, 870 change_mask, 871 name->full_name); 872 } 873 874 return pvfs_dosattrib_save(pvfs, name, -1); 875} 876 877