--- sourceforge.net/trunk/rdesktop/disk.c 2004/02/03 14:03:42 596 +++ sourceforge.net/trunk/rdesktop/disk.c 2004/02/15 21:19:28 608 @@ -24,6 +24,7 @@ #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define FILE_ATTRIBUTE_DEVICE 0x00000040 +#define FILE_ATTRIBUTE_UNKNOWNXXX0 0x00000060 /* ??? ACTION i.e. 0x860 == compress this file ? */ #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 @@ -33,6 +34,21 @@ #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 +#define FILE_FLAG_OPEN_NO_RECALL 0x00100000 +#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 +#define FILE_FLAG_POSIX_SEMANTICS 0x01000000 +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 /* sometimes used to create a directory */ +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 +#define FILE_FLAG_WRITE_THROUGH 0x80000000 + +#define FILE_SHARE_READ 0x01 +#define FILE_SHARE_WRITE 0x02 +#define FILE_SHARE_DELETE 0x04 + #define FILE_BASIC_INFORMATION 0x04 #define FILE_STANDARD_INFORMATION 0x05 @@ -68,7 +84,7 @@ #define SOLARIS #endif -#ifdef SOLARIS +#if (defined(SOLARIS) || defined(__hpux)) #define DIRFD(a) ((a)->dd_fd) #else #define DIRFD(a) (dirfd(a)) @@ -82,7 +98,11 @@ #include #include /* errno */ -#if defined(SOLARIS) +#include +#include /* ctime */ + + +#if (defined(SOLARIS) || defined (__hpux) || defined(__BEOS__)) #include /* solaris statvfs */ #define STATFS_FN(path, buf) (statvfs(path,buf)) #define STATFS_T statvfs @@ -117,6 +137,21 @@ } g_fileinfo[MAX_OPEN_FILES]; + +time_t +get_create_time(struct stat *st) +{ + time_t ret, ret1; + + ret = MIN(st->st_ctime, st->st_mtime); + ret1 = MIN(ret, st->st_atime); + + if (ret1 != (time_t) 0) + return ret1; + + return ret; +} + /* Convert seconds since 1970 to a filetime */ void seconds_since_1970_to_filetime(time_t seconds, uint32 * high, uint32 * low) @@ -128,12 +163,29 @@ *high = (uint32) (ticks >> 32); } +/* Convert seconds since 1970 back to filetime */ +time_t +convert_1970_to_filetime(uint32 high, uint32 low) +{ + unsigned long long ticks; + time_t val; + + ticks = low + (((unsigned long long) high) << 32); + ticks /= 10000000; + ticks -= 11644473600LL; + + val = (time_t) ticks; + return (val); + +} + + /* Enumeration of devices from rdesktop.c */ /* returns numer of units found and initialized. */ /* optarg looks like ':h:=/mnt/floppy,b:=/mnt/usbdevice1' */ /* when it arrives to this function. */ int -disk_enum_devices(int *id, char *optarg) +disk_enum_devices(uint32 * id, char *optarg) { char *pos = optarg; char *pos2; @@ -172,12 +224,14 @@ DIR *dirp; int flags, mode; char path[256]; + struct stat filestat; handle = 0; dirp = NULL; flags = 0; mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + if (filename[strlen(filename) - 1] == '/') filename[strlen(filename) - 1] = 0; sprintf(path, "%s%s", g_rdpdr_device[device_id].local_path, filename); @@ -215,26 +269,26 @@ break; } - /* the sad part is that we can't trust this flag */ - /* directories aren't always marked */ - if (flags_and_attributes ^ FILE_DIRECTORY_FILE) + //printf("Open: \"%s\" flags: %u, accessmask: %u sharemode: %u create disp: %u\n", path, flags_and_attributes, accessmask, sharemode, create_disposition); + + // Get information about file and set that flag ourselfs + if ((stat(path, &filestat) == 0) && (S_ISDIR(filestat.st_mode))) { - if (accessmask & GENERIC_ALL - || (accessmask & GENERIC_READ && accessmask & GENERIC_WRITE)) - { - flags |= O_RDWR; - } - else if ((accessmask & GENERIC_WRITE) && !(accessmask & GENERIC_READ)) - { - flags |= O_WRONLY; - } + if (flags_and_attributes & FILE_NON_DIRECTORY_FILE) + return STATUS_FILE_IS_A_DIRECTORY; else + flags_and_attributes |= FILE_DIRECTORY_FILE; + } + + if (flags_and_attributes & FILE_DIRECTORY_FILE) + { + if (flags & O_CREAT) { - flags |= O_RDONLY; + mkdir(path, mode); } - handle = open(path, flags, mode); - if (handle == -1) + dirp = opendir(path); + if (!dirp) { switch (errno) { @@ -248,45 +302,38 @@ default: - perror("open"); + perror("opendir"); return STATUS_NO_SUCH_FILE; } } - - /* all read and writes of files should be non blocking */ - if (fcntl(handle, F_SETFL, O_NONBLOCK) == -1) - perror("fcntl"); + handle = DIRFD(dirp); } - - /* since we can't trust the FILE_DIRECTORY_FILE flag */ - /* we need to double check that the file isn't a dir */ - if (handle != 0) + else { - /* Must check if this file isn't actually a directory */ - struct stat filestat; - // Get information about file and set that flag ourselfs - if ((fstat(handle, &filestat) == 0) && (S_ISDIR(filestat.st_mode))) + if (accessmask & GENERIC_ALL + || (accessmask & GENERIC_READ && accessmask & GENERIC_WRITE)) { - flags_and_attributes |= FILE_DIRECTORY_FILE; - close(handle); - handle = 0; + flags |= O_RDWR; } - } - - - if (flags_and_attributes & FILE_DIRECTORY_FILE) - { - if (flags & O_CREAT) + else if ((accessmask & GENERIC_WRITE) && !(accessmask & GENERIC_READ)) { - mkdir(path, mode); + flags |= O_WRONLY; + } + else + { + flags |= O_RDONLY; } - dirp = opendir(path); - if (!dirp) + handle = open(path, flags, mode); + if (handle == -1) { switch (errno) { + case EISDIR: + + return STATUS_FILE_IS_A_DIRECTORY; + case EACCES: return STATUS_ACCESS_DENIED; @@ -294,14 +341,16 @@ case ENOENT: return STATUS_NO_SUCH_FILE; - default: - perror("opendir"); + perror("open"); return STATUS_NO_SUCH_FILE; } } - handle = DIRFD(dirp); + + /* all read and writes of files should be non blocking */ + if (fcntl(handle, F_SETFL, O_NONBLOCK) == -1) + perror("fcntl"); } if (handle >= MAX_OPEN_FILES) @@ -315,7 +364,6 @@ g_fileinfo[handle].pdir = dirp; g_fileinfo[handle].device_id = device_id; g_fileinfo[handle].flags_and_attributes = flags_and_attributes; -// printf("create: attrib: %u handle %u\n", g_fileinfo[handle].flags_and_attributes, handle ); strncpy(g_fileinfo[handle].path, path, 255); *phandle = handle; @@ -347,6 +395,7 @@ { int n; +#if 0 /* browsing dir ???? */ /* each request is 24 bytes */ if (g_fileinfo[handle].flags_and_attributes & FILE_DIRECTORY_FILE) @@ -354,6 +403,7 @@ *result = 0; return STATUS_SUCCESS; } +#endif if (offset) lseek(handle, offset, SEEK_SET); @@ -361,9 +411,15 @@ if (n < 0) { - perror("read"); *result = 0; - return STATUS_INVALID_PARAMETER; + switch (errno) + { + case EISDIR: + return STATUS_FILE_IS_A_DIRECTORY; + default: + perror("read"); + return STATUS_INVALID_PARAMETER; + } } *result = n; @@ -385,7 +441,13 @@ { perror("write"); *result = 0; - return STATUS_ACCESS_DENIED; + switch (errno) + { + case ENOSPC: + return STATUS_DISK_FULL; + default: + return STATUS_ACCESS_DENIED; + } } *result = n; @@ -413,21 +475,26 @@ // Set file attributes file_attributes = 0; if (S_ISDIR(filestat.st_mode)) - { file_attributes |= FILE_ATTRIBUTE_DIRECTORY; - } + filename = 1 + strrchr(path, '/'); if (filename && filename[0] == '.') - { file_attributes |= FILE_ATTRIBUTE_HIDDEN; - } + + if (!file_attributes) + file_attributes |= FILE_ATTRIBUTE_NORMAL; + + if (!(filestat.st_mode & S_IWUSR)) + file_attributes |= FILE_ATTRIBUTE_READONLY; // Return requested data switch (info_class) { case 4: /* FileBasicInformation */ - - out_uint8s(out, 8); //create_time not available; + seconds_since_1970_to_filetime(get_create_time(&filestat), &ft_high, + &ft_low); + out_uint32_le(out, ft_low); //create_access_time + out_uint32_le(out, ft_high); seconds_since_1970_to_filetime(filestat.st_atime, &ft_high, &ft_low); out_uint32_le(out, ft_low); //last_access_time @@ -437,7 +504,10 @@ out_uint32_le(out, ft_low); //last_write_time out_uint32_le(out, ft_high); - out_uint8s(out, 8); //unknown zero + seconds_since_1970_to_filetime(filestat.st_ctime, &ft_high, &ft_low); + out_uint32_le(out, ft_low); //last_change_time + out_uint32_le(out, ft_high); + out_uint32_le(out, file_attributes); break; @@ -473,13 +543,92 @@ char newname[256], fullpath[256]; struct fileinfo *pfinfo; + int mode; + struct stat filestat; + time_t write_time, change_time, access_time, mod_time; + struct utimbuf tvs; + pfinfo = &(g_fileinfo[handle]); switch (info_class) { case 4: /* FileBasicInformation */ + write_time = change_time = access_time = 0; + + in_uint8s(in, 4); /* Handle of root dir? */ + in_uint8s(in, 24); /* unknown */ + + // CreationTime + in_uint32_le(in, ft_low); + in_uint32_le(in, ft_high); + + // AccessTime + in_uint32_le(in, ft_low); + in_uint32_le(in, ft_high); + if (ft_low || ft_high) + access_time = convert_1970_to_filetime(ft_high, ft_low); + + // WriteTime + in_uint32_le(in, ft_low); + in_uint32_le(in, ft_high); + if (ft_low || ft_high) + write_time = convert_1970_to_filetime(ft_high, ft_low); + + // ChangeTime + in_uint32_le(in, ft_low); + in_uint32_le(in, ft_high); + if (ft_low || ft_high) + change_time = convert_1970_to_filetime(ft_high, ft_low); + + in_uint32_le(in, file_attributes); + + if (fstat(handle, &filestat)) + return STATUS_ACCESS_DENIED; + + tvs.modtime = filestat.st_mtime; + tvs.actime = filestat.st_atime; + if (access_time) + tvs.actime = access_time; + + + if (write_time || change_time) + mod_time = MIN(write_time, change_time); + else + mod_time = write_time ? write_time : change_time; + + if (mod_time) + tvs.modtime = mod_time; + + + if (access_time || write_time || change_time) + { +#if WITH_DEBUG_RDP5 + printf("FileBasicInformation access time %s", + ctime(&tvs.actime)); + printf("FileBasicInformation modification time %s", + ctime(&tvs.modtime)); +#endif + if (utime(pfinfo->path, &tvs)) + return STATUS_ACCESS_DENIED; + } + + if (!file_attributes) + break; // not valid - // Probably safe to ignore + mode = filestat.st_mode; + + if (file_attributes & FILE_ATTRIBUTE_READONLY) + mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + mode |= S_IWUSR; + + mode &= 0777; +#if WITH_DEBUG_RDP5 + printf("FileBasicInformation set access mode 0%o", mode); +#endif + + if (fchmod(handle, mode)) + return STATUS_ACCESS_DENIED; break; case 10: /* FileRenameInformation */ @@ -511,9 +660,17 @@ case 13: /* FileDispositionInformation */ //unimpl("IRP Set File Information class: FileDispositionInformation\n"); - // in_uint32_le(in, delete_on_close); + + //in_uint32_le(in, delete_on_close); // disk_close(handle); - unlink(pfinfo->path); + if ((pfinfo->flags_and_attributes & FILE_DIRECTORY_FILE)) // remove a directory + { + if (rmdir(pfinfo->path) < 0) + return STATUS_ACCESS_DENIED; + } + else if (unlink(pfinfo->path) < 0) // unlink a file + return STATUS_ACCESS_DENIED; + break; case 19: /* FileAllocationInformation */ @@ -522,10 +679,14 @@ break; case 20: /* FileEndOfFileInformation */ + in_uint8s(in, 28); /* unknown */ + in_uint32_le(in, length); /* file size */ + + printf("FileEndOfFileInformation length = %d\n", length); + // ???????????? unimpl("IRP Set File Information class: FileEndOfFileInformation\n"); break; - default: unimpl("IRP Set File Information class: 0x%x\n", info_class); @@ -545,7 +706,7 @@ volume = "RDESKTOP"; fs_type = "RDPFS"; - if (STATFS_FN(pfinfo->path, &stat_fs) != 0) /* FIXME: statfs is not portable */ + if (STATFS_FN(pfinfo->path, &stat_fs) != 0) { perror("statfs"); return STATUS_ACCESS_DENIED; @@ -624,17 +785,14 @@ // find next dirent matching pattern pdirent = readdir(pdir); while (pdirent && fnmatch(pfinfo->pattern, pdirent->d_name, 0) != 0) - { pdirent = readdir(pdir); - } if (pdirent == NULL) - { return STATUS_NO_MORE_FILES; - } // Get information for directory entry sprintf(fullpath, "%s/%s", dirname, pdirent->d_name); + /* JIF printf("Stat: %s\n", fullpath); */ if (stat(fullpath, &fstat)) @@ -648,10 +806,17 @@ file_attributes |= FILE_ATTRIBUTE_DIRECTORY; if (pdirent->d_name[0] == '.') file_attributes |= FILE_ATTRIBUTE_HIDDEN; + if (!file_attributes) + file_attributes |= FILE_ATTRIBUTE_NORMAL; + if (!(fstat.st_mode & S_IWUSR)) + file_attributes |= FILE_ATTRIBUTE_READONLY; // Return requested information out_uint8s(out, 8); //unknown zero - out_uint8s(out, 8); //create_time not available in posix; + + seconds_since_1970_to_filetime(get_create_time(&fstat), &ft_high, &ft_low); + out_uint32_le(out, ft_low); // create time + out_uint32_le(out, ft_high); seconds_since_1970_to_filetime(fstat.st_atime, &ft_high, &ft_low); out_uint32_le(out, ft_low); //last_access_time @@ -661,7 +826,10 @@ out_uint32_le(out, ft_low); //last_write_time out_uint32_le(out, ft_high); - out_uint8s(out, 8); //unknown zero + seconds_since_1970_to_filetime(fstat.st_ctime, &ft_high, &ft_low); + out_uint32_le(out, ft_low); //change_write_time + out_uint32_le(out, ft_high); + out_uint32_le(out, fstat.st_size); //filesize low out_uint32_le(out, 0); //filesize high out_uint32_le(out, fstat.st_size); //filesize low @@ -683,10 +851,38 @@ return STATUS_SUCCESS; } + + +static NTSTATUS +disk_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out) +{ + uint32 result; + + if (((request >> 16) != 20) || ((request >> 16) != 9)) + return STATUS_INVALID_PARAMETER; + + /* extract operation */ + request >>= 2; + request &= 0xfff; + + printf("DISK IOCTL %d\n", request); + + switch (request) + { + case 25: // ? + case 42: // ? + default: + unimpl("DISK IOCTL %d\n", request); + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + DEVICE_FNS disk_fns = { disk_create, disk_close, disk_read, disk_write, - NULL /* device_control */ + disk_device_control /* device_control */ };