1 /* -*- c-basic-offset: 8 -*- 2 rdesktop: A Remote Desktop Protocol client. 3 Disk Redirection 4 Copyright (C) Jeroen Meijer 2003 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 along 17 with this program; if not, write to the Free Software Foundation, Inc., 18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 */ 20 21 #include "disk.h" 22 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <unistd.h> 26 #include <fcntl.h> /* open, close */ 27 #include <dirent.h> /* opendir, closedir, readdir */ 28 #include <fnmatch.h> 29 #include <errno.h> /* errno */ 30 #include <stdio.h> 31 32 #include <utime.h> 33 #include <time.h> /* ctime */ 34 35 #if (defined(HAVE_DIRFD) || (HAVE_DECL_DIRFD == 1)) 36 #define DIRFD(a) (dirfd(a)) 37 #else 38 #define DIRFD(a) ((a)->DIR_FD_MEMBER_NAME) 39 #endif 40 41 /* TODO: Fix mntent-handling for solaris 42 * #include <sys/mntent.h> */ 43 #if (defined(HAVE_MNTENT_H) && defined(HAVE_SETMNTENT)) 44 #include <mntent.h> 45 #define MNTENT_PATH "/etc/mtab" 46 #define USE_SETMNTENT 47 #endif 48 49 #ifdef HAVE_SYS_VFS_H 50 #include <sys/vfs.h> 51 #endif 52 53 #ifdef HAVE_SYS_STATVFS_H 54 #include <sys/statvfs.h> 55 #endif 56 57 #ifdef HAVE_SYS_STATFS_H 58 #include <sys/statfs.h> 59 #endif 60 61 #ifdef HAVE_SYS_PARAM_H 62 #include <sys/param.h> 63 #endif 64 65 #ifdef HAVE_SYS_MOUNT_H 66 #include <sys/mount.h> 67 #endif 68 69 #include "rdesktop.h" 70 71 #ifdef STAT_STATFS3_OSF1 72 #define STATFS_FN(path, buf) (statfs(path,buf,sizeof(buf))) 73 #define STATFS_T statfs 74 #define USE_STATFS 75 #endif 76 77 #ifdef STAT_STATVFS 78 #define STATFS_FN(path, buf) (statvfs(path,buf)) 79 #define STATFS_T statvfs 80 #define USE_STATVFS 81 #endif 82 83 #ifdef STAT_STATVFS64 84 #define STATFS_FN(path, buf) (statvfs64(path,buf)) 85 #define STATFS_T statvfs64 86 #define USE_STATVFS 87 #endif 88 89 #if (defined(STAT_STATFS2_FS_DATA) || defined(STAT_STATFS2_BSIZE) || defined(STAT_STATFS2_FSIZE)) 90 #define STATFS_FN(path, buf) (statfs(path,buf)) 91 #define STATFS_T statfs 92 #define USE_STATFS 93 #endif 94 95 #ifdef STAT_STATFS4 96 #define STATFS_FN(path, buf) (statfs(path,buf,sizeof(buf),0)) 97 #define STATFS_T statfs 98 #define USE_STATFS 99 #endif 100 101 #if ((defined(USE_STATFS) && defined(HAVE_STRUCT_STATFS_F_NAMEMAX)) || (defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_NAMEMAX))) 102 #define F_NAMELEN(buf) ((buf).f_namemax) 103 #endif 104 105 #if ((defined(USE_STATFS) && defined(HAVE_STRUCT_STATFS_F_NAMELEN)) || (defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_NAMELEN))) 106 #define F_NAMELEN(buf) ((buf).f_namelen) 107 #endif 108 109 #ifndef F_NAMELEN 110 #define F_NAMELEN(buf) (255) 111 #endif 112 113 /* Dummy statfs fallback */ 114 #ifndef STATFS_T 115 struct dummy_statfs_t 116 { 117 long f_bfree; 118 long f_bsize; 119 long f_blocks; 120 int f_namelen; 121 int f_namemax; 122 }; 123 124 static int 125 dummy_statfs(struct dummy_statfs_t *buf) 126 { 127 buf->f_blocks = 262144; 128 buf->f_bfree = 131072; 129 buf->f_bsize = 512; 130 buf->f_namelen = 255; 131 buf->f_namemax = 255; 132 133 return 0; 134 } 135 136 #define STATFS_T dummy_statfs_t 137 #define STATFS_FN(path,buf) (dummy_statfs(buf)) 138 #endif 139 140 typedef struct 141 { 142 char name[PATH_MAX]; 143 char label[PATH_MAX]; 144 unsigned long serial; 145 char type[PATH_MAX]; 146 } FsInfoType; 147 148 static NTSTATUS NotifyInfo(RDPCLIENT * This, NTHANDLE handle, uint32 info_class, NOTIFY * p); 149 150 static time_t 151 get_create_time(struct stat *st) 152 { 153 time_t ret, ret1; 154 155 ret = MIN(st->st_ctime, st->st_mtime); 156 ret1 = MIN(ret, st->st_atime); 157 158 if (ret1 != (time_t) 0) 159 return ret1; 160 161 return ret; 162 } 163 164 /* Convert seconds since 1970 to a filetime */ 165 static void 166 seconds_since_1970_to_filetime(time_t seconds, uint32 * high, uint32 * low) 167 { 168 unsigned long long ticks; 169 170 ticks = (seconds + 11644473600LL) * 10000000; 171 *low = (uint32) ticks; 172 *high = (uint32) (ticks >> 32); 173 } 174 175 /* Convert seconds since 1970 back to filetime */ 176 static time_t 177 convert_1970_to_filetime(uint32 high, uint32 low) 178 { 179 unsigned long long ticks; 180 time_t val; 181 182 ticks = low + (((unsigned long long) high) << 32); 183 ticks /= 10000000; 184 ticks -= 11644473600LL; 185 186 val = (time_t) ticks; 187 return (val); 188 189 } 190 191 /* A wrapper for ftruncate which supports growing files, even if the 192 native ftruncate doesn't. This is needed on Linux FAT filesystems, 193 for example. */ 194 static int 195 ftruncate_growable(int fd, off_t length) 196 { 197 int ret; 198 off_t pos; 199 static const char zero = 0; 200 201 /* Try the simple method first */ 202 if ((ret = ftruncate(fd, length)) != -1) 203 { 204 return ret; 205 } 206 207 /* 208 * Some kind of error. Perhaps we were trying to grow. Retry 209 * in a safe way. 210 */ 211 212 /* Get current position */ 213 if ((pos = lseek(fd, 0, SEEK_CUR)) == -1) 214 { 215 perror("lseek"); 216 return -1; 217 } 218 219 /* Seek to new size */ 220 if (lseek(fd, length, SEEK_SET) == -1) 221 { 222 perror("lseek"); 223 return -1; 224 } 225 226 /* Write a zero */ 227 if (write(fd, &zero, 1) == -1) 228 { 229 perror("write"); 230 return -1; 231 } 232 233 /* Truncate. This shouldn't fail. */ 234 if (ftruncate(fd, length) == -1) 235 { 236 perror("ftruncate"); 237 return -1; 238 } 239 240 /* Restore position */ 241 if (lseek(fd, pos, SEEK_SET) == -1) 242 { 243 perror("lseek"); 244 return -1; 245 } 246 247 return 0; 248 } 249 250 /* Just like open(2), but if a open with O_EXCL fails, retry with 251 GUARDED semantics. This might be necessary because some filesystems 252 (such as NFS filesystems mounted from a unfsd server) doesn't 253 support O_EXCL. GUARDED semantics are subject to race conditions, 254 but we can live with that. 255 */ 256 static int 257 open_weak_exclusive(const char *pathname, int flags, mode_t mode) 258 { 259 int ret; 260 struct stat statbuf; 261 262 ret = open(pathname, flags, mode); 263 if (ret != -1 || !(flags & O_EXCL)) 264 { 265 /* Success, or not using O_EXCL */ 266 return ret; 267 } 268 269 /* An error occured, and we are using O_EXCL. In case the FS 270 doesn't support O_EXCL, some kind of error will be 271 returned. Unfortunately, we don't know which one. Linux 272 2.6.8 seems to return 524, but I cannot find a documented 273 #define for this case. So, we'll return only on errors that 274 we know aren't related to O_EXCL. */ 275 switch (errno) 276 { 277 case EACCES: 278 case EEXIST: 279 case EINTR: 280 case EISDIR: 281 case ELOOP: 282 case ENAMETOOLONG: 283 case ENOENT: 284 case ENOTDIR: 285 return ret; 286 } 287 288 /* Retry with GUARDED semantics */ 289 if (stat(pathname, &statbuf) != -1) 290 { 291 /* File exists */ 292 errno = EEXIST; 293 return -1; 294 } 295 else 296 { 297 return open(pathname, flags & ~O_EXCL, mode); 298 } 299 } 300 301 /* Enumeration of devices from rdesktop.c */ 302 /* returns numer of units found and initialized. */ 303 /* optarg looks like ':h=/mnt/floppy,b=/mnt/usbdevice1' */ 304 /* when it arrives to this function. */ 305 int 306 disk_enum_devices(RDPCLIENT * This, uint32 * id, char *optarg) 307 { 308 char *pos = optarg; 309 char *pos2; 310 int count = 0; 311 312 /* skip the first colon */ 313 optarg++; 314 while ((pos = next_arg(optarg, ',')) && *id < RDPDR_MAX_DEVICES) 315 { 316 pos2 = next_arg(optarg, '='); 317 318 strncpy(This->rdpdr_device[*id].name, optarg, sizeof(This->rdpdr_device[*id].name) - 1); 319 if (strlen(optarg) > (sizeof(This->rdpdr_device[*id].name) - 1)) 320 fprintf(stderr, "share name %s truncated to %s\n", optarg, 321 This->rdpdr_device[*id].name); 322 323 This->rdpdr_device[*id].local_path = (char *) xmalloc(strlen(pos2) + 1); 324 strcpy(This->rdpdr_device[*id].local_path, pos2); 325 This->rdpdr_device[*id].device_type = DEVICE_TYPE_DISK; 326 count++; 327 (*id)++; 328 329 optarg = pos; 330 } 331 return count; 332 } 333 334 /* Opens or creates a file or directory */ 335 static NTSTATUS 336 disk_create(RDPCLIENT * This, uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition, 337 uint32 flags_and_attributes, char *filename, NTHANDLE * phandle) 338 { 339 NTHANDLE handle; 340 DIR *dirp; 341 int flags, mode; 342 char path[PATH_MAX]; 343 struct stat filestat; 344 345 handle = 0; 346 dirp = NULL; 347 flags = 0; 348 mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 349 350 if (*filename && filename[strlen(filename) - 1] == '/') 351 filename[strlen(filename) - 1] = 0; 352 sprintf(path, "%s%s", This->rdpdr_device[device_id].local_path, filename); 353 354 switch (create_disposition) 355 { 356 case CREATE_ALWAYS: 357 358 /* Delete existing file/link. */ 359 unlink(path); 360 flags |= O_CREAT; 361 break; 362 363 case CREATE_NEW: 364 365 /* If the file already exists, then fail. */ 366 flags |= O_CREAT | O_EXCL; 367 break; 368 369 case OPEN_ALWAYS: 370 371 /* Create if not already exists. */ 372 flags |= O_CREAT; 373 break; 374 375 case OPEN_EXISTING: 376 377 /* Default behaviour */ 378 break; 379 380 case TRUNCATE_EXISTING: 381 382 /* If the file does not exist, then fail. */ 383 flags |= O_TRUNC; 384 break; 385 } 386 387 /*printf("Open: \"%s\" flags: %X, accessmask: %X sharemode: %X create disp: %X\n", path, flags_and_attributes, accessmask, sharemode, create_disposition); */ 388 389 /* Get information about file and set that flag ourselfs */ 390 if ((stat(path, &filestat) == 0) && (S_ISDIR(filestat.st_mode))) 391 { 392 if (flags_and_attributes & FILE_NON_DIRECTORY_FILE) 393 return STATUS_FILE_IS_A_DIRECTORY; 394 else 395 flags_and_attributes |= FILE_DIRECTORY_FILE; 396 } 397 398 if (flags_and_attributes & FILE_DIRECTORY_FILE) 399 { 400 if (flags & O_CREAT) 401 { 402 mkdir(path, mode); 403 } 404 405 dirp = opendir(path); 406 if (!dirp) 407 { 408 switch (errno) 409 { 410 case EACCES: 411 412 return STATUS_ACCESS_DENIED; 413 414 case ENOENT: 415 416 return STATUS_NO_SUCH_FILE; 417 418 default: 419 420 perror("opendir"); 421 return STATUS_NO_SUCH_FILE; 422 } 423 } 424 handle = DIRFD(dirp); 425 } 426 else 427 { 428 429 if (accessmask & GENERIC_ALL 430 || (accessmask & GENERIC_READ && accessmask & GENERIC_WRITE)) 431 { 432 flags |= O_RDWR; 433 } 434 else if ((accessmask & GENERIC_WRITE) && !(accessmask & GENERIC_READ)) 435 { 436 flags |= O_WRONLY; 437 } 438 else 439 { 440 flags |= O_RDONLY; 441 } 442 443 handle = open_weak_exclusive(path, flags, mode); 444 if (handle == -1) 445 { 446 switch (errno) 447 { 448 case EISDIR: 449 450 return STATUS_FILE_IS_A_DIRECTORY; 451 452 case EACCES: 453 454 return STATUS_ACCESS_DENIED; 455 456 case ENOENT: 457 458 return STATUS_NO_SUCH_FILE; 459 case EEXIST: 460 461 return STATUS_OBJECT_NAME_COLLISION; 462 default: 463 464 perror("open"); 465 return STATUS_NO_SUCH_FILE; 466 } 467 } 468 469 /* all read and writes of files should be non blocking */ 470 if (fcntl(handle, F_SETFL, O_NONBLOCK) == -1) 471 perror("fcntl"); 472 } 473 474 if (handle >= MAX_OPEN_FILES) 475 { 476 error("Maximum number of open files (%s) reached. Increase MAX_OPEN_FILES!\n", 477 handle); 478 exit(1); 479 } 480 481 if (dirp) 482 This->fileinfo[handle].pdir = dirp; 483 else 484 This->fileinfo[handle].pdir = NULL; 485 486 This->fileinfo[handle].device_id = device_id; 487 This->fileinfo[handle].flags_and_attributes = flags_and_attributes; 488 This->fileinfo[handle].accessmask = accessmask; 489 strncpy(This->fileinfo[handle].path, path, PATH_MAX - 1); 490 This->fileinfo[handle].delete_on_close = False; 491 This->notify_stamp = True; 492 493 *phandle = handle; 494 return STATUS_SUCCESS; 495 } 496 497 static NTSTATUS 498 disk_close(RDPCLIENT * This, NTHANDLE handle) 499 { 500 struct fileinfo *pfinfo; 501 502 pfinfo = &(This->fileinfo[handle]); 503 504 This->notify_stamp = True; 505 506 rdpdr_abort_io(This, handle, 0, STATUS_CANCELLED); 507 508 if (pfinfo->pdir) 509 { 510 if (closedir(pfinfo->pdir) < 0) 511 { 512 perror("closedir"); 513 return STATUS_INVALID_HANDLE; 514 } 515 516 if (pfinfo->delete_on_close) 517 if (rmdir(pfinfo->path) < 0) 518 { 519 perror(pfinfo->path); 520 return STATUS_ACCESS_DENIED; 521 } 522 pfinfo->delete_on_close = False; 523 } 524 else 525 { 526 if (close(handle) < 0) 527 { 528 perror("close"); 529 return STATUS_INVALID_HANDLE; 530 } 531 if (pfinfo->delete_on_close) 532 if (unlink(pfinfo->path) < 0) 533 { 534 perror(pfinfo->path); 535 return STATUS_ACCESS_DENIED; 536 } 537 538 pfinfo->delete_on_close = False; 539 } 540 541 return STATUS_SUCCESS; 542 } 543 544 static NTSTATUS 545 disk_read(RDPCLIENT * This, NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result) 546 { 547 int n; 548 549 #if 0 550 /* browsing dir ???? */ 551 /* each request is 24 bytes */ 552 if (This->fileinfo[handle].flags_and_attributes & FILE_DIRECTORY_FILE) 553 { 554 *result = 0; 555 return STATUS_SUCCESS; 556 } 557 #endif 558 559 lseek(handle, offset, SEEK_SET); 560 561 n = read(handle, data, length); 562 563 if (n < 0) 564 { 565 *result = 0; 566 switch (errno) 567 { 568 case EISDIR: 569 /* Implement 24 Byte directory read ?? 570 with STATUS_NOT_IMPLEMENTED server doesn't read again */ 571 /* return STATUS_FILE_IS_A_DIRECTORY; */ 572 return STATUS_NOT_IMPLEMENTED; 573 default: 574 perror("read"); 575 return STATUS_INVALID_PARAMETER; 576 } 577 } 578 579 *result = n; 580 581 return STATUS_SUCCESS; 582 } 583 584 static NTSTATUS 585 disk_write(RDPCLIENT * This, NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result) 586 { 587 int n; 588 589 lseek(handle, offset, SEEK_SET); 590 591 n = write(handle, data, length); 592 593 if (n < 0) 594 { 595 perror("write"); 596 *result = 0; 597 switch (errno) 598 { 599 case ENOSPC: 600 return STATUS_DISK_FULL; 601 default: 602 return STATUS_ACCESS_DENIED; 603 } 604 } 605 606 *result = n; 607 608 return STATUS_SUCCESS; 609 } 610 611 NTSTATUS 612 disk_query_information(RDPCLIENT * This, NTHANDLE handle, uint32 info_class, STREAM out) 613 { 614 uint32 file_attributes, ft_high, ft_low; 615 struct stat filestat; 616 char *path, *filename; 617 618 path = This->fileinfo[handle].path; 619 620 /* Get information about file */ 621 if (fstat(handle, &filestat) != 0) 622 { 623 perror("stat"); 624 out_uint8(out, 0); 625 return STATUS_ACCESS_DENIED; 626 } 627 628 /* Set file attributes */ 629 file_attributes = 0; 630 if (S_ISDIR(filestat.st_mode)) 631 file_attributes |= FILE_ATTRIBUTE_DIRECTORY; 632 633 filename = 1 + strrchr(path, '/'); 634 if (filename && filename[0] == '.') 635 file_attributes |= FILE_ATTRIBUTE_HIDDEN; 636 637 if (!file_attributes) 638 file_attributes |= FILE_ATTRIBUTE_NORMAL; 639 640 if (!(filestat.st_mode & S_IWUSR)) 641 file_attributes |= FILE_ATTRIBUTE_READONLY; 642 643 /* Return requested data */ 644 switch (info_class) 645 { 646 case FileBasicInformation: 647 seconds_since_1970_to_filetime(get_create_time(&filestat), &ft_high, 648 &ft_low); 649 out_uint32_le(out, ft_low); /* create_access_time */ 650 out_uint32_le(out, ft_high); 651 652 seconds_since_1970_to_filetime(filestat.st_atime, &ft_high, &ft_low); 653 out_uint32_le(out, ft_low); /* last_access_time */ 654 out_uint32_le(out, ft_high); 655 656 seconds_since_1970_to_filetime(filestat.st_mtime, &ft_high, &ft_low); 657 out_uint32_le(out, ft_low); /* last_write_time */ 658 out_uint32_le(out, ft_high); 659 660 seconds_since_1970_to_filetime(filestat.st_ctime, &ft_high, &ft_low); 661 out_uint32_le(out, ft_low); /* last_change_time */ 662 out_uint32_le(out, ft_high); 663 664 out_uint32_le(out, file_attributes); 665 break; 666 667 case FileStandardInformation: 668 669 out_uint32_le(out, filestat.st_size); /* Allocation size */ 670 out_uint32_le(out, 0); 671 out_uint32_le(out, filestat.st_size); /* End of file */ 672 out_uint32_le(out, 0); 673 out_uint32_le(out, filestat.st_nlink); /* Number of links */ 674 out_uint8(out, 0); /* Delete pending */ 675 out_uint8(out, S_ISDIR(filestat.st_mode) ? 1 : 0); /* Directory */ 676 break; 677 678 case FileObjectIdInformation: 679 680 out_uint32_le(out, file_attributes); /* File Attributes */ 681 out_uint32_le(out, 0); /* Reparse Tag */ 682 break; 683 684 default: 685 686 unimpl("IRP Query (File) Information class: 0x%x\n", info_class); 687 return STATUS_INVALID_PARAMETER; 688 } 689 return STATUS_SUCCESS; 690 } 691 692 NTSTATUS 693 disk_set_information(RDPCLIENT * This, NTHANDLE handle, uint32 info_class, STREAM in, STREAM out) 694 { 695 uint32 length, file_attributes, ft_high, ft_low, delete_on_close; 696 char newname[PATH_MAX], fullpath[PATH_MAX]; 697 struct fileinfo *pfinfo; 698 int mode; 699 struct stat filestat; 700 time_t write_time, change_time, access_time, mod_time; 701 struct utimbuf tvs; 702 struct STATFS_T stat_fs; 703 704 pfinfo = &(This->fileinfo[handle]); 705 This->notify_stamp = True; 706 707 switch (info_class) 708 { 709 case FileBasicInformation: 710 write_time = change_time = access_time = 0; 711 712 in_uint8s(in, 4); /* Handle of root dir? */ 713 in_uint8s(in, 24); /* unknown */ 714 715 /* CreationTime */ 716 in_uint32_le(in, ft_low); 717 in_uint32_le(in, ft_high); 718 719 /* AccessTime */ 720 in_uint32_le(in, ft_low); 721 in_uint32_le(in, ft_high); 722 if (ft_low || ft_high) 723 access_time = convert_1970_to_filetime(ft_high, ft_low); 724 725 /* WriteTime */ 726 in_uint32_le(in, ft_low); 727 in_uint32_le(in, ft_high); 728 if (ft_low || ft_high) 729 write_time = convert_1970_to_filetime(ft_high, ft_low); 730 731 /* ChangeTime */ 732 in_uint32_le(in, ft_low); 733 in_uint32_le(in, ft_high); 734 if (ft_low || ft_high) 735 change_time = convert_1970_to_filetime(ft_high, ft_low); 736 737 in_uint32_le(in, file_attributes); 738 739 if (fstat(handle, &filestat)) 740 return STATUS_ACCESS_DENIED; 741 742 tvs.modtime = filestat.st_mtime; 743 tvs.actime = filestat.st_atime; 744 if (access_time) 745 tvs.actime = access_time; 746 747 748 if (write_time || change_time) 749 mod_time = MIN(write_time, change_time); 750 else 751 mod_time = write_time ? write_time : change_time; 752 753 if (mod_time) 754 tvs.modtime = mod_time; 755 756 757 if (access_time || write_time || change_time) 758 { 759 #if WITH_DEBUG_RDP5 760 printf("FileBasicInformation access time %s", 761 ctime(&tvs.actime)); 762 printf("FileBasicInformation modification time %s", 763 ctime(&tvs.modtime)); 764 #endif 765 if (utime(pfinfo->path, &tvs) && errno != EPERM) 766 return STATUS_ACCESS_DENIED; 767 } 768 769 if (!file_attributes) 770 break; /* not valid */ 771 772 mode = filestat.st_mode; 773 774 if (file_attributes & FILE_ATTRIBUTE_READONLY) 775 mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); 776 else 777 mode |= S_IWUSR; 778 779 mode &= 0777; 780 #if WITH_DEBUG_RDP5 781 printf("FileBasicInformation set access mode 0%o", mode); 782 #endif 783 784 if (fchmod(handle, mode)) 785 return STATUS_ACCESS_DENIED; 786 787 break; 788 789 case FileRenameInformation: 790 791 in_uint8s(in, 4); /* Handle of root dir? */ 792 in_uint8s(in, 0x1a); /* unknown */ 793 in_uint32_le(in, length); 794 795 if (length && (length / 2) < 256) 796 { 797 rdp_in_unistr(This, in, newname, length); 798 convert_to_unix_filename(newname); 799 } 800 else 801 { 802 return STATUS_INVALID_PARAMETER; 803 } 804 805 sprintf(fullpath, "%s%s", This->rdpdr_device[pfinfo->device_id].local_path, 806 newname); 807 808 if (rename(pfinfo->path, fullpath) != 0) 809 { 810 perror("rename"); 811 return STATUS_ACCESS_DENIED; 812 } 813 break; 814 815 case FileDispositionInformation: 816 /* As far as I understand it, the correct 817 thing to do here is to *schedule* a delete, 818 so it will be deleted when the file is 819 closed. Subsequent 820 FileDispositionInformation requests with 821 DeleteFile set to FALSE should unschedule 822 the delete. See 823 http://www.osronline.com/article.cfm?article=245. */ 824 825 in_uint32_le(in, delete_on_close); 826 827 if (delete_on_close || 828 (pfinfo-> 829 accessmask & (FILE_DELETE_ON_CLOSE | FILE_COMPLETE_IF_OPLOCKED))) 830 { 831 pfinfo->delete_on_close = True; 832 } 833 834 break; 835 836 case FileAllocationInformation: 837 /* Fall through to FileEndOfFileInformation, 838 which uses ftrunc. This is like Samba with 839 "strict allocation = false", and means that 840 we won't detect out-of-quota errors, for 841 example. */ 842 843 case FileEndOfFileInformation: 844 in_uint8s(in, 28); /* unknown */ 845 in_uint32_le(in, length); /* file size */ 846 847 /* prevents start of writing if not enough space left on device */ 848 if (STATFS_FN(This->rdpdr_device[pfinfo->device_id].local_path, &stat_fs) == 0) 849 if (stat_fs.f_bfree * stat_fs.f_bsize < length) 850 return STATUS_DISK_FULL; 851 852 if (ftruncate_growable(handle, length) != 0) 853 { 854 return STATUS_DISK_FULL; 855 } 856 857 break; 858 default: 859 860 unimpl("IRP Set File Information class: 0x%x\n", info_class); 861 return STATUS_INVALID_PARAMETER; 862 } 863 return STATUS_SUCCESS; 864 } 865 866 NTSTATUS 867 disk_check_notify(RDPCLIENT * This, NTHANDLE handle) 868 { 869 struct fileinfo *pfinfo; 870 NTSTATUS status = STATUS_PENDING; 871 872 NOTIFY notify; 873 874 pfinfo = &(This->fileinfo[handle]); 875 if (!pfinfo->pdir) 876 return STATUS_INVALID_DEVICE_REQUEST; 877 878 879 880 status = NotifyInfo(This, handle, pfinfo->info_class, ¬ify); 881 882 if (status != STATUS_PENDING) 883 return status; 884 885 if (memcmp(&pfinfo->notify, ¬ify, sizeof(NOTIFY))) 886 { 887 /*printf("disk_check_notify found changed event\n"); */ 888 memcpy(&pfinfo->notify, ¬ify, sizeof(NOTIFY)); 889 status = STATUS_NOTIFY_ENUM_DIR; 890 } 891 892 return status; 893 894 895 } 896 897 NTSTATUS 898 disk_create_notify(RDPCLIENT * This, NTHANDLE handle, uint32 info_class) 899 { 900 901 struct fileinfo *pfinfo; 902 NTSTATUS ret = STATUS_PENDING; 903 904 /* printf("start disk_create_notify info_class %X\n", info_class); */ 905 906 pfinfo = &(This->fileinfo[handle]); 907 pfinfo->info_class = info_class; 908 909 ret = NotifyInfo(This, handle, info_class, &pfinfo->notify); 910 911 if (info_class & 0x1000) 912 { /* ???? */ 913 if (ret == STATUS_PENDING) 914 return STATUS_SUCCESS; 915 } 916 917 /* printf("disk_create_notify: num_entries %d\n", pfinfo->notify.num_entries); */ 918 919 920 return ret; 921 922 } 923 924 static NTSTATUS 925 NotifyInfo(RDPCLIENT * This, NTHANDLE handle, uint32 info_class, NOTIFY * p) 926 { 927 struct fileinfo *pfinfo; 928 struct stat buf; 929 struct dirent *dp; 930 char *fullname; 931 DIR *dpr; 932 933 pfinfo = &(This->fileinfo[handle]); 934 if (fstat(handle, &buf) < 0) 935 { 936 perror("NotifyInfo"); 937 return STATUS_ACCESS_DENIED; 938 } 939 p->modify_time = buf.st_mtime; 940 p->status_time = buf.st_ctime; 941 p->num_entries = 0; 942 p->total_time = 0; 943 944 945 dpr = opendir(pfinfo->path); 946 if (!dpr) 947 { 948 perror("NotifyInfo"); 949 return STATUS_ACCESS_DENIED; 950 } 951 952 953 while ((dp = readdir(dpr))) 954 { 955 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 956 continue; 957 p->num_entries++; 958 fullname = (char *) xmalloc(strlen(pfinfo->path) + strlen(dp->d_name) + 2); 959 sprintf(fullname, "%s/%s", pfinfo->path, dp->d_name); 960 961 if (!stat(fullname, &buf)) 962 { 963 p->total_time += (buf.st_mtime + buf.st_ctime); 964 } 965 966 xfree(fullname); 967 } 968 closedir(dpr); 969 970 return STATUS_PENDING; 971 } 972 973 static FsInfoType * 974 FsVolumeInfo(char *fpath) 975 { 976 977 static FsInfoType info; 978 #ifdef USE_SETMNTENT 979 FILE *fdfs; 980 struct mntent *e; 981 #endif 982 983 /* initialize */ 984 memset(&info, 0, sizeof(info)); 985 strcpy(info.label, "RDESKTOP"); 986 strcpy(info.type, "RDPFS"); 987 988 #ifdef USE_SETMNTENT 989 fdfs = setmntent(MNTENT_PATH, "r"); 990 if (!fdfs) 991 return &info; 992 993 while ((e = getmntent(fdfs))) 994 { 995 if (str_startswith(e->mnt_dir, fpath)) 996 { 997 strcpy(info.type, e->mnt_type); 998 strcpy(info.name, e->mnt_fsname); 999 if (strstr(e->mnt_opts, "vfat") || strstr(e->mnt_opts, "iso9660")) 1000 { 1001 int fd = open(e->mnt_fsname, O_RDONLY); 1002 if (fd >= 0) 1003 { 1004 unsigned char buf[512]; 1005 memset(buf, 0, sizeof(buf)); 1006 if (strstr(e->mnt_opts, "vfat")) 1007 /*FAT*/ 1008 { 1009 strcpy(info.type, "vfat"); 1010 read(fd, buf, sizeof(buf)); 1011 info.serial = 1012 (buf[42] << 24) + (buf[41] << 16) + 1013 (buf[40] << 8) + buf[39]; 1014 strncpy(info.label, buf + 43, 10); 1015 info.label[10] = '\0'; 1016 } 1017 else if (lseek(fd, 32767, SEEK_SET) >= 0) /* ISO9660 */ 1018 { 1019 read(fd, buf, sizeof(buf)); 1020 strncpy(info.label, buf + 41, 32); 1021 info.label[32] = '\0'; 1022 /* info.Serial = (buf[128]<<24)+(buf[127]<<16)+(buf[126]<<8)+buf[125]; */ 1023 } 1024 close(fd); 1025 } 1026 } 1027 } 1028 } 1029 endmntent(fdfs); 1030 #else 1031 /* initialize */ 1032 memset(&info, 0, sizeof(info)); 1033 strcpy(info.label, "RDESKTOP"); 1034 strcpy(info.type, "RDPFS"); 1035 1036 #endif 1037 return &info; 1038 } 1039 1040 1041 NTSTATUS 1042 disk_query_volume_information(RDPCLIENT * This, NTHANDLE handle, uint32 info_class, STREAM out) 1043 { 1044 struct STATFS_T stat_fs; 1045 struct fileinfo *pfinfo; 1046 FsInfoType *fsinfo; 1047 1048 pfinfo = &(This->fileinfo[handle]); 1049 1050 if (STATFS_FN(pfinfo->path, &stat_fs) != 0) 1051 { 1052 perror("statfs"); 1053 return STATUS_ACCESS_DENIED; 1054 } 1055 1056 fsinfo = FsVolumeInfo(pfinfo->path); 1057 1058 switch (info_class) 1059 { 1060 case FileFsVolumeInformation: 1061 1062 out_uint32_le(out, 0); /* volume creation time low */ 1063 out_uint32_le(out, 0); /* volume creation time high */ 1064 out_uint32_le(out, fsinfo->serial); /* serial */ 1065 1066 out_uint32_le(out, 2 * strlen(fsinfo->label)); /* length of string */ 1067 1068 out_uint8(out, 0); /* support objects? */ 1069 rdp_out_unistr(This, out, fsinfo->label, 2 * strlen(fsinfo->label) - 2); 1070 break; 1071 1072 case FileFsSizeInformation: 1073 1074 out_uint32_le(out, stat_fs.f_blocks); /* Total allocation units low */ 1075 out_uint32_le(out, 0); /* Total allocation high units */ 1076 out_uint32_le(out, stat_fs.f_bfree); /* Available allocation units */ 1077 out_uint32_le(out, 0); /* Available allowcation units */ 1078 out_uint32_le(out, stat_fs.f_bsize / 0x200); /* Sectors per allocation unit */ 1079 out_uint32_le(out, 0x200); /* Bytes per sector */ 1080 break; 1081 1082 case FileFsAttributeInformation: 1083 1084 out_uint32_le(out, FS_CASE_SENSITIVE | FS_CASE_IS_PRESERVED); /* fs attributes */ 1085 out_uint32_le(out, F_NAMELEN(stat_fs)); /* max length of filename */ 1086 1087 out_uint32_le(out, 2 * strlen(fsinfo->type)); /* length of fs_type */ 1088 rdp_out_unistr(This, out, fsinfo->type, 2 * strlen(fsinfo->type) - 2); 1089 break; 1090 1091 case FileFsLabelInformation: 1092 case FileFsDeviceInformation: 1093 case FileFsControlInformation: 1094 case FileFsFullSizeInformation: 1095 case FileFsObjectIdInformation: 1096 case FileFsMaximumInformation: 1097 1098 default: 1099 1100 unimpl("IRP Query Volume Information class: 0x%x\n", info_class); 1101 return STATUS_INVALID_PARAMETER; 1102 } 1103 return STATUS_SUCCESS; 1104 } 1105 1106 NTSTATUS 1107 disk_query_directory(RDPCLIENT * This, NTHANDLE handle, uint32 info_class, char *pattern, STREAM out) 1108 { 1109 uint32 file_attributes, ft_low, ft_high; 1110 char *dirname, fullpath[PATH_MAX]; 1111 DIR *pdir; 1112 struct dirent *pdirent; 1113 struct stat fstat; 1114 struct fileinfo *pfinfo; 1115 1116 pfinfo = &(This->fileinfo[handle]); 1117 pdir = pfinfo->pdir; 1118 dirname = pfinfo->path; 1119 file_attributes = 0; 1120 1121 switch (info_class) 1122 { 1123 case FileBothDirectoryInformation: 1124 1125 /* If a search pattern is received, remember this pattern, and restart search */ 1126 if (pattern[0] != 0) 1127 { 1128 strncpy(pfinfo->pattern, 1 + strrchr(pattern, '/'), PATH_MAX - 1); 1129 rewinddir(pdir); 1130 } 1131 1132 /* find next dirent matching pattern */ 1133 pdirent = readdir(pdir); 1134 while (pdirent && fnmatch(pfinfo->pattern, pdirent->d_name, 0) != 0) 1135 pdirent = readdir(pdir); 1136 1137 if (pdirent == NULL) 1138 return STATUS_NO_MORE_FILES; 1139 1140 /* Get information for directory entry */ 1141 sprintf(fullpath, "%s/%s", dirname, pdirent->d_name); 1142 1143 if (stat(fullpath, &fstat)) 1144 { 1145 switch (errno) 1146 { 1147 case ENOENT: 1148 case ELOOP: 1149 case EACCES: 1150 /* These are non-fatal errors. */ 1151 memset(&fstat, 0, sizeof(fstat)); 1152 break; 1153 default: 1154 /* Fatal error. By returning STATUS_NO_SUCH_FILE, 1155 the directory list operation will be aborted */ 1156 perror(fullpath); 1157 out_uint8(out, 0); 1158 return STATUS_NO_SUCH_FILE; 1159 } 1160 } 1161 1162 if (S_ISDIR(fstat.st_mode)) 1163 file_attributes |= FILE_ATTRIBUTE_DIRECTORY; 1164 if (pdirent->d_name[0] == '.') 1165 file_attributes |= FILE_ATTRIBUTE_HIDDEN; 1166 if (!file_attributes) 1167 file_attributes |= FILE_ATTRIBUTE_NORMAL; 1168 if (!(fstat.st_mode & S_IWUSR)) 1169 file_attributes |= FILE_ATTRIBUTE_READONLY; 1170 1171 /* Return requested information */ 1172 out_uint8s(out, 8); /* unknown zero */ 1173 1174 seconds_since_1970_to_filetime(get_create_time(&fstat), &ft_high, &ft_low); 1175 out_uint32_le(out, ft_low); /* create time */ 1176 out_uint32_le(out, ft_high); 1177 1178 seconds_since_1970_to_filetime(fstat.st_atime, &ft_high, &ft_low); 1179 out_uint32_le(out, ft_low); /* last_access_time */ 1180 out_uint32_le(out, ft_high); 1181 1182 seconds_since_1970_to_filetime(fstat.st_mtime, &ft_high, &ft_low); 1183 out_uint32_le(out, ft_low); /* last_write_time */ 1184 out_uint32_le(out, ft_high); 1185 1186 seconds_since_1970_to_filetime(fstat.st_ctime, &ft_high, &ft_low); 1187 out_uint32_le(out, ft_low); /* change_write_time */ 1188 out_uint32_le(out, ft_high); 1189 1190 out_uint32_le(out, fstat.st_size); /* filesize low */ 1191 out_uint32_le(out, 0); /* filesize high */ 1192 out_uint32_le(out, fstat.st_size); /* filesize low */ 1193 out_uint32_le(out, 0); /* filesize high */ 1194 out_uint32_le(out, file_attributes); 1195 out_uint8(out, 2 * strlen(pdirent->d_name) + 2); /* unicode length */ 1196 out_uint8s(out, 7); /* pad? */ 1197 out_uint8(out, 0); /* 8.3 file length */ 1198 out_uint8s(out, 2 * 12); /* 8.3 unicode length */ 1199 rdp_out_unistr(This, out, pdirent->d_name, 2 * strlen(pdirent->d_name)); 1200 break; 1201 1202 default: 1203 /* FIXME: Support FileDirectoryInformation, 1204 FileFullDirectoryInformation, and 1205 FileNamesInformation */ 1206 1207 unimpl("IRP Query Directory sub: 0x%x\n", info_class); 1208 return STATUS_INVALID_PARAMETER; 1209 } 1210 1211 return STATUS_SUCCESS; 1212 } 1213 1214 1215 1216 static NTSTATUS 1217 disk_device_control(RDPCLIENT * This, NTHANDLE handle, uint32 request, STREAM in, STREAM out) 1218 { 1219 if (((request >> 16) != 20) || ((request >> 16) != 9)) 1220 return STATUS_INVALID_PARAMETER; 1221 1222 /* extract operation */ 1223 request >>= 2; 1224 request &= 0xfff; 1225 1226 printf("DISK IOCTL %d\n", request); 1227 1228 switch (request) 1229 { 1230 case 25: /* ? */ 1231 case 42: /* ? */ 1232 default: 1233 unimpl("DISK IOCTL %d\n", request); 1234 return STATUS_INVALID_PARAMETER; 1235 } 1236 1237 return STATUS_SUCCESS; 1238 } 1239 1240 DEVICE_FNS disk_fns = { 1241 disk_create, 1242 disk_close, 1243 disk_read, 1244 disk_write, 1245 disk_device_control /* device_control */ 1246 }; 1247