VirtualBox

source: vbox/trunk/src/VBox/RDP/client/disk.c@ 10076

Last change on this file since 10076 was 9902, checked in by vboxsync, 16 years ago

Added rdesktop 1.6.0.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette