--- sourceforge.net/trunk/rdesktop/disk.c 2004/01/21 21:51:59 572 +++ sourceforge.net/trunk/rdesktop/disk.c 2004/02/06 10:41:34 600 @@ -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 @@ -82,12 +98,28 @@ #include #include /* errno */ -#ifdef SOLARIS +#include +#include /* ctime */ + + +#if defined(SOLARIS) #include /* solaris statvfs */ -#define HAVE_STATVFS +#define STATFS_FN(path, buf) (statvfs(path,buf)) +#define STATFS_T statvfs +#define F_NAMELEN(buf) ((buf).f_namemax) + +#elif (defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)) +#include +#include +#define STATFS_FN(path, buf) (statfs(path,buf)) +#define STATFS_T statfs +#define F_NAMELEN(buf) (NAME_MAX) + #else #include /* linux statfs */ -#define HAVE_STATFS +#define STATFS_FN(path, buf) (statfs(path,buf)) +#define STATFS_T statfs +#define F_NAMELEN(buf) ((buf).f_namelen) #endif #include "rdesktop.h" @@ -105,10 +137,20 @@ } g_fileinfo[MAX_OPEN_FILES]; -struct fsinfo + +time_t +get_create_time(struct stat *st) { - uint32 f_blocks, f_bfree, f_bsize, f_namelen; -}; + 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 @@ -121,6 +163,23 @@ *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' */ @@ -156,7 +215,7 @@ return count; } -/* Opens of creates a file or directory */ +/* Opens or creates a file or directory */ NTSTATUS disk_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition, uint32 flags_and_attributes, char *filename, HANDLE * phandle) @@ -165,16 +224,17 @@ 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); - //printf("Open: %s\n", path); switch (create_disposition) { @@ -209,6 +269,17 @@ break; } + //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 (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) @@ -239,6 +310,7 @@ } else { + if (accessmask & GENERIC_ALL || (accessmask & GENERIC_READ && accessmask & GENERIC_WRITE)) { @@ -258,6 +330,10 @@ { switch (errno) { + case EISDIR: + + return STATUS_FILE_IS_A_DIRECTORY; + case EACCES: return STATUS_ACCESS_DENIED; @@ -265,13 +341,16 @@ case ENOENT: return STATUS_NO_SUCH_FILE; - default: perror("open"); 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"); } if (handle >= MAX_OPEN_FILES) @@ -316,15 +395,31 @@ { int n; +#if 0 + /* browsing dir ???? */ + /* each request is 24 bytes */ + if (g_fileinfo[handle].flags_and_attributes & FILE_DIRECTORY_FILE) + { + *result = 0; + return STATUS_SUCCESS; + } +#endif + if (offset) lseek(handle, offset, SEEK_SET); n = read(handle, data, length); 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; @@ -346,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; @@ -374,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 @@ -398,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; @@ -434,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 */ - // Probably safe to ignore + // 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 + + 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 */ @@ -483,10 +671,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); @@ -495,44 +687,18 @@ return STATUS_SUCCESS; } -int fsstat(const char *path, struct fsinfo *buf) -{ - int ret; -#if defined(HAVE_STATFS) - struct statfs statbuf; -#elif defined(HAVE_STATVFS) - struct statvfs statbuf; -#endif - -#if defined(HAVE_STATFS) - ret = statfs(path, &statbuf); - buf->f_namelen = statbuf.f_namelen; -#elif defined(HAVE_STATVFS) - ret = statvfs(path, &statbuf); - buf->f_namelen = statbuf.f_namemax; -#else - ret=-1; -#endif - - buf->f_blocks = statbuf.f_blocks; - buf->f_bfree = statbuf.f_bfree; - buf->f_bsize = statbuf.f_bsize; - - return ret; -} - NTSTATUS disk_query_volume_information(HANDLE handle, uint32 info_class, STREAM out) { char *volume, *fs_type; - struct fsinfo stat_fs; + struct STATFS_T stat_fs; struct fileinfo *pfinfo; pfinfo = &(g_fileinfo[handle]); volume = "RDESKTOP"; fs_type = "RDPFS"; - if (fsstat(pfinfo->path, &stat_fs) != 0) /* FIXME: statfs is not portable */ + if (STATFS_FN(pfinfo->path, &stat_fs) != 0) { perror("statfs"); return STATUS_ACCESS_DENIED; @@ -563,7 +729,7 @@ case 5: /* FileFsAttributeInformation */ out_uint32_le(out, FS_CASE_SENSITIVE | FS_CASE_IS_PRESERVED); /* fs attributes */ - out_uint32_le(out, stat_fs.f_namelen); /* max length of filename */ + out_uint32_le(out, F_NAMELEN(stat_fs)); /* max length of filename */ out_uint32_le(out, 2 * strlen(fs_type)); /* length of fs_type */ rdp_out_unistr(out, fs_type, 2 * strlen(fs_type) - 2); break; @@ -611,18 +777,15 @@ // 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 + + /* JIF printf("Stat: %s\n", fullpath); */ if (stat(fullpath, &fstat)) { @@ -635,10 +798,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 @@ -648,7 +818,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 @@ -670,10 +843,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 */ };