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, &notify);
881 
882 	if (status != STATUS_PENDING)
883 		return status;
884 
885 	if (memcmp(&pfinfo->notify, &notify, sizeof(NOTIFY)))
886 	{
887 		/*printf("disk_check_notify found changed event\n"); */
888 		memcpy(&pfinfo->notify, &notify, 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