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