Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Server-side file management |
| 3 | * |
| 4 | * Copyright (C) 1998 Alexandre Julliard |
| 5 | */ |
| 6 | |
Patrik Stridvall | 9633632 | 1999-10-24 22:13:47 +0000 | [diff] [blame] | 7 | #include "config.h" |
| 8 | |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 9 | #include <assert.h> |
| 10 | #include <fcntl.h> |
| 11 | #include <stdio.h> |
David Luyer | ee517e8 | 1999-02-28 12:27:56 +0000 | [diff] [blame] | 12 | #include <string.h> |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 13 | #include <stdlib.h> |
Marcus Meissner | 8ba68fb | 1999-01-01 18:42:17 +0000 | [diff] [blame] | 14 | #include <errno.h> |
Howard Abrams | 1327748 | 1999-07-10 13:16:29 +0000 | [diff] [blame] | 15 | #ifdef HAVE_SYS_ERRNO_H |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 16 | #include <sys/errno.h> |
Howard Abrams | 1327748 | 1999-07-10 13:16:29 +0000 | [diff] [blame] | 17 | #endif |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 18 | #include <sys/stat.h> |
| 19 | #include <sys/time.h> |
| 20 | #include <sys/types.h> |
| 21 | #include <time.h> |
| 22 | #include <unistd.h> |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 23 | #include <utime.h> |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 24 | |
| 25 | #include "winerror.h" |
Michael Veksler | f935c59 | 1999-02-09 15:49:39 +0000 | [diff] [blame] | 26 | #include "winbase.h" |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 27 | |
| 28 | #include "handle.h" |
| 29 | #include "thread.h" |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 30 | #include "request.h" |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 31 | |
| 32 | struct file |
| 33 | { |
Alexandre Julliard | 57e1131 | 1999-05-16 16:59:38 +0000 | [diff] [blame] | 34 | struct object obj; /* object header */ |
Alexandre Julliard | 57e1131 | 1999-05-16 16:59:38 +0000 | [diff] [blame] | 35 | struct file *next; /* next file in hashing list */ |
| 36 | char *name; /* file name */ |
| 37 | unsigned int access; /* file access (GENERIC_READ/WRITE) */ |
| 38 | unsigned int flags; /* flags (FILE_FLAG_*) */ |
| 39 | unsigned int sharing; /* file sharing mode */ |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 40 | }; |
| 41 | |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 42 | #define NAME_HASH_SIZE 37 |
| 43 | |
| 44 | static struct file *file_hash[NAME_HASH_SIZE]; |
| 45 | |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 46 | static void file_dump( struct object *obj, int verbose ); |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 47 | static int file_get_poll_events( struct object *obj ); |
Alexandre Julliard | 1ab243b | 2000-12-19 02:12:45 +0000 | [diff] [blame] | 48 | static int file_get_fd( struct object *obj ); |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 49 | static int file_flush( struct object *obj ); |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 50 | static int file_get_info( struct object *obj, struct get_file_info_request *req ); |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 51 | static void file_destroy( struct object *obj ); |
| 52 | |
| 53 | static const struct object_ops file_ops = |
| 54 | { |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 55 | sizeof(struct file), /* size */ |
| 56 | file_dump, /* dump */ |
| 57 | default_poll_add_queue, /* add_queue */ |
| 58 | default_poll_remove_queue, /* remove_queue */ |
| 59 | default_poll_signaled, /* signaled */ |
| 60 | no_satisfied, /* satisfied */ |
| 61 | file_get_poll_events, /* get_poll_events */ |
| 62 | default_poll_event, /* poll_event */ |
Alexandre Julliard | 1ab243b | 2000-12-19 02:12:45 +0000 | [diff] [blame] | 63 | file_get_fd, /* get_fd */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 64 | file_flush, /* flush */ |
| 65 | file_get_info, /* get_file_info */ |
| 66 | file_destroy /* destroy */ |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 67 | }; |
| 68 | |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 69 | |
| 70 | static int get_name_hash( const char *name ) |
| 71 | { |
| 72 | int hash = 0; |
Eric Pouech | bf2b765 | 1999-11-15 00:07:30 +0000 | [diff] [blame] | 73 | while (*name) hash ^= (unsigned char)*name++; |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 74 | return hash % NAME_HASH_SIZE; |
| 75 | } |
| 76 | |
| 77 | /* check if the desired access is possible without violating */ |
| 78 | /* the sharing mode of other opens of the same file */ |
| 79 | static int check_sharing( const char *name, int hash, unsigned int access, |
| 80 | unsigned int sharing ) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 81 | { |
| 82 | struct file *file; |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 83 | unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; |
| 84 | unsigned int existing_access = 0; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 85 | |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 86 | for (file = file_hash[hash]; file; file = file->next) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 87 | { |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 88 | if (strcmp( file->name, name )) continue; |
| 89 | existing_sharing &= file->sharing; |
| 90 | existing_access |= file->access; |
| 91 | } |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 92 | if ((access & GENERIC_READ) && !(existing_sharing & FILE_SHARE_READ)) goto error; |
| 93 | if ((access & GENERIC_WRITE) && !(existing_sharing & FILE_SHARE_WRITE)) goto error; |
| 94 | if ((existing_access & GENERIC_READ) && !(sharing & FILE_SHARE_READ)) goto error; |
| 95 | if ((existing_access & GENERIC_WRITE) && !(sharing & FILE_SHARE_WRITE)) goto error; |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 96 | return 1; |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 97 | error: |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 98 | set_error( STATUS_SHARING_VIOLATION ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 99 | return 0; |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 100 | } |
| 101 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 102 | /* create a file from a file descriptor */ |
| 103 | /* if the function fails the fd is closed */ |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 104 | static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing, |
| 105 | unsigned int attrs ) |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 106 | { |
| 107 | struct file *file; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 108 | if ((file = alloc_object( &file_ops, fd ))) |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 109 | { |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 110 | file->name = NULL; |
| 111 | file->next = NULL; |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 112 | file->access = access; |
| 113 | file->flags = attrs; |
| 114 | file->sharing = sharing; |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 115 | } |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 116 | return file; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 117 | } |
| 118 | |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 119 | |
| 120 | static struct file *create_file( const char *nameptr, size_t len, unsigned int access, |
| 121 | unsigned int sharing, int create, unsigned int attrs ) |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 122 | { |
| 123 | struct file *file; |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 124 | int hash, flags; |
| 125 | struct stat st; |
| 126 | char *name; |
| 127 | int fd = -1; |
| 128 | |
| 129 | if (!(name = mem_alloc( len + 1 ))) return NULL; |
| 130 | memcpy( name, nameptr, len ); |
| 131 | name[len] = 0; |
| 132 | |
| 133 | /* check sharing mode */ |
| 134 | hash = get_name_hash( name ); |
| 135 | if (!check_sharing( name, hash, access, sharing )) goto error; |
| 136 | |
| 137 | switch(create) |
| 138 | { |
| 139 | case CREATE_NEW: flags = O_CREAT | O_EXCL; break; |
| 140 | case CREATE_ALWAYS: flags = O_CREAT | O_TRUNC; break; |
| 141 | case OPEN_ALWAYS: flags = O_CREAT; break; |
| 142 | case TRUNCATE_EXISTING: flags = O_TRUNC; break; |
| 143 | case OPEN_EXISTING: flags = 0; break; |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 144 | default: set_error( STATUS_INVALID_PARAMETER ); goto error; |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 145 | } |
| 146 | switch(access & (GENERIC_READ | GENERIC_WRITE)) |
| 147 | { |
| 148 | case 0: break; |
| 149 | case GENERIC_READ: flags |= O_RDONLY; break; |
| 150 | case GENERIC_WRITE: flags |= O_WRONLY; break; |
| 151 | case GENERIC_READ|GENERIC_WRITE: flags |= O_RDWR; break; |
| 152 | } |
| 153 | |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 154 | /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */ |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 155 | if ((fd = open( name, flags | O_NONBLOCK, |
| 156 | (attrs & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666 )) == -1) |
| 157 | goto file_error; |
| 158 | /* refuse to open a directory */ |
| 159 | if (fstat( fd, &st ) == -1) goto file_error; |
| 160 | if (S_ISDIR(st.st_mode)) |
| 161 | { |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 162 | set_error( STATUS_ACCESS_DENIED ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 163 | goto error; |
| 164 | } |
| 165 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 166 | if (!(file = create_file_for_fd( fd, access, sharing, attrs ))) |
| 167 | { |
| 168 | free( name ); |
| 169 | return NULL; |
| 170 | } |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 171 | file->name = name; |
| 172 | file->next = file_hash[hash]; |
| 173 | file_hash[hash] = file; |
| 174 | return file; |
| 175 | |
| 176 | file_error: |
| 177 | file_set_error(); |
| 178 | error: |
| 179 | if (fd != -1) close( fd ); |
| 180 | free( name ); |
| 181 | return NULL; |
| 182 | } |
| 183 | |
| 184 | /* Create an anonymous Unix file */ |
| 185 | int create_anonymous_file(void) |
| 186 | { |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 187 | char *name; |
| 188 | int fd; |
| 189 | |
| 190 | do |
| 191 | { |
| 192 | if (!(name = tmpnam(NULL))) |
| 193 | { |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 194 | set_error( STATUS_TOO_MANY_OPENED_FILES ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 195 | return -1; |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 196 | } |
| 197 | fd = open( name, O_CREAT | O_EXCL | O_RDWR, 0600 ); |
| 198 | } while ((fd == -1) && (errno == EEXIST)); |
| 199 | if (fd == -1) |
| 200 | { |
| 201 | file_set_error(); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 202 | return -1; |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 203 | } |
| 204 | unlink( name ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 205 | return fd; |
| 206 | } |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 207 | |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 208 | /* Create a temp file for anonymous mappings */ |
| 209 | struct file *create_temp_file( int access ) |
| 210 | { |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 211 | int fd; |
| 212 | |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 213 | if ((fd = create_anonymous_file()) == -1) return NULL; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 214 | return create_file_for_fd( fd, access, 0, 0 ); |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 215 | } |
| 216 | |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 217 | static void file_dump( struct object *obj, int verbose ) |
| 218 | { |
| 219 | struct file *file = (struct file *)obj; |
| 220 | assert( obj->ops == &file_ops ); |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 221 | fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->obj.fd, file->flags, file->name ); |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 222 | } |
| 223 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 224 | static int file_get_poll_events( struct object *obj ) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 225 | { |
| 226 | struct file *file = (struct file *)obj; |
Alexandre Julliard | 57e1131 | 1999-05-16 16:59:38 +0000 | [diff] [blame] | 227 | int events = 0; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 228 | assert( obj->ops == &file_ops ); |
Alexandre Julliard | 247b8ae | 1999-12-13 00:16:44 +0000 | [diff] [blame] | 229 | if (file->access & GENERIC_READ) events |= POLLIN; |
| 230 | if (file->access & GENERIC_WRITE) events |= POLLOUT; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 231 | return events; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 232 | } |
| 233 | |
Alexandre Julliard | 1ab243b | 2000-12-19 02:12:45 +0000 | [diff] [blame] | 234 | static int file_get_fd( struct object *obj ) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 235 | { |
| 236 | struct file *file = (struct file *)obj; |
| 237 | assert( obj->ops == &file_ops ); |
Alexandre Julliard | 63411db | 2000-12-22 21:12:36 +0000 | [diff] [blame^] | 238 | return file->obj.fd; |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 239 | } |
| 240 | |
| 241 | static int file_flush( struct object *obj ) |
| 242 | { |
| 243 | int ret; |
| 244 | struct file *file = (struct file *)grab_object(obj); |
| 245 | assert( obj->ops == &file_ops ); |
| 246 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 247 | ret = (fsync( file->obj.fd ) != -1); |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 248 | if (!ret) file_set_error(); |
| 249 | release_object( file ); |
| 250 | return ret; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 251 | } |
| 252 | |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 253 | static int file_get_info( struct object *obj, struct get_file_info_request *req ) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 254 | { |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 255 | struct stat st; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 256 | struct file *file = (struct file *)obj; |
| 257 | assert( obj->ops == &file_ops ); |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 258 | |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 259 | if (fstat( file->obj.fd, &st ) == -1) |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 260 | { |
| 261 | file_set_error(); |
| 262 | return 0; |
| 263 | } |
| 264 | if (S_ISCHR(st.st_mode) || S_ISFIFO(st.st_mode) || |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 265 | S_ISSOCK(st.st_mode) || isatty(file->obj.fd)) req->type = FILE_TYPE_CHAR; |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 266 | else req->type = FILE_TYPE_DISK; |
| 267 | if (S_ISDIR(st.st_mode)) req->attr = FILE_ATTRIBUTE_DIRECTORY; |
| 268 | else req->attr = FILE_ATTRIBUTE_ARCHIVE; |
| 269 | if (!(st.st_mode & S_IWUSR)) req->attr |= FILE_ATTRIBUTE_READONLY; |
| 270 | req->access_time = st.st_atime; |
| 271 | req->write_time = st.st_mtime; |
| 272 | req->size_high = 0; |
| 273 | req->size_low = S_ISDIR(st.st_mode) ? 0 : st.st_size; |
| 274 | req->links = st.st_nlink; |
| 275 | req->index_high = st.st_dev; |
| 276 | req->index_low = st.st_ino; |
| 277 | req->serial = 0; /* FIXME */ |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 278 | return 1; |
| 279 | } |
| 280 | |
| 281 | static void file_destroy( struct object *obj ) |
| 282 | { |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 283 | struct file *file = (struct file *)obj; |
| 284 | assert( obj->ops == &file_ops ); |
| 285 | |
Alexandre Julliard | 638f169 | 1999-01-17 16:32:32 +0000 | [diff] [blame] | 286 | if (file->name) |
| 287 | { |
| 288 | /* remove it from the hashing list */ |
| 289 | struct file **pptr = &file_hash[get_name_hash( file->name )]; |
| 290 | while (*pptr && *pptr != file) pptr = &(*pptr)->next; |
| 291 | assert( *pptr ); |
| 292 | *pptr = (*pptr)->next; |
Juergen Schmied | cddfcce | 1999-02-14 11:20:07 +0000 | [diff] [blame] | 293 | if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name ); |
Alexandre Julliard | 638f169 | 1999-01-17 16:32:32 +0000 | [diff] [blame] | 294 | free( file->name ); |
| 295 | } |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 296 | } |
| 297 | |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 298 | /* set the last error depending on errno */ |
| 299 | void file_set_error(void) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 300 | { |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 301 | switch (errno) |
| 302 | { |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 303 | case EAGAIN: set_error( STATUS_SHARING_VIOLATION ); break; |
| 304 | case EBADF: set_error( STATUS_INVALID_HANDLE ); break; |
| 305 | case ENOSPC: set_error( STATUS_DISK_FULL ); break; |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 306 | case EACCES: |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 307 | case EPERM: set_error( STATUS_ACCESS_DENIED ); break; |
| 308 | case EROFS: set_error( STATUS_MEDIA_WRITE_PROTECTED ); break; |
| 309 | case EBUSY: set_error( STATUS_FILE_LOCK_CONFLICT ); break; |
| 310 | case ENOENT: set_error( STATUS_NO_SUCH_FILE ); break; |
| 311 | case EISDIR: set_error( 0xc0010000 | ERROR_CANNOT_MAKE /* FIXME */ ); break; |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 312 | case ENFILE: |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 313 | case EMFILE: set_error( STATUS_NO_MORE_FILES ); break; |
| 314 | case EEXIST: set_error( STATUS_OBJECT_NAME_COLLISION ); break; |
| 315 | case EINVAL: set_error( STATUS_INVALID_PARAMETER ); break; |
| 316 | case ESPIPE: set_error( 0xc0010000 | ERROR_SEEK /* FIXME */ ); break; |
| 317 | case ENOTEMPTY: set_error( STATUS_DIRECTORY_NOT_EMPTY ); break; |
| 318 | case EIO: set_error( STATUS_ACCESS_VIOLATION ); break; |
| 319 | default: perror("file_set_error"); set_error( ERROR_UNKNOWN /* FIXME */ ); break; |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 320 | } |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 321 | } |
| 322 | |
Alexandre Julliard | 61ec6c1 | 1999-11-29 02:17:08 +0000 | [diff] [blame] | 323 | struct file *get_file_obj( struct process *process, int handle, unsigned int access ) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 324 | { |
Alexandre Julliard | 61ec6c1 | 1999-11-29 02:17:08 +0000 | [diff] [blame] | 325 | return (struct file *)get_handle_obj( process, handle, access, &file_ops ); |
Alexandre Julliard | a8b8d9c | 1999-01-01 16:59:27 +0000 | [diff] [blame] | 326 | } |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 327 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 328 | static int set_file_pointer( int handle, int *low, int *high, int whence ) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 329 | { |
| 330 | struct file *file; |
| 331 | int result; |
| 332 | |
Hidenori Takeshima | 66791af | 2000-07-31 23:26:50 +0000 | [diff] [blame] | 333 | if ((*low >= 0 && *high != 0) || (*low < 0 && *high != -1)) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 334 | { |
Hidenori Takeshima | 66791af | 2000-07-31 23:26:50 +0000 | [diff] [blame] | 335 | fprintf( stderr, "set_file_pointer: offset > 2Gb not supported yet\n" ); |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 336 | set_error( STATUS_INVALID_PARAMETER ); |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 337 | return 0; |
| 338 | } |
| 339 | |
Alexandre Julliard | a8b8d9c | 1999-01-01 16:59:27 +0000 | [diff] [blame] | 340 | if (!(file = get_file_obj( current->process, handle, 0 ))) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 341 | return 0; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 342 | if ((result = lseek( file->obj.fd, *low, whence )) == -1) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 343 | { |
| 344 | /* Check for seek before start of file */ |
| 345 | if ((errno == EINVAL) && (whence != SEEK_SET) && (*low < 0)) |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 346 | set_error( 0xc0010000 | ERROR_NEGATIVE_SEEK /* FIXME */ ); |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 347 | else |
| 348 | file_set_error(); |
| 349 | release_object( file ); |
| 350 | return 0; |
| 351 | } |
| 352 | *low = result; |
| 353 | release_object( file ); |
| 354 | return 1; |
| 355 | } |
| 356 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 357 | static int truncate_file( int handle ) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 358 | { |
| 359 | struct file *file; |
| 360 | int result; |
| 361 | |
Alexandre Julliard | a8b8d9c | 1999-01-01 16:59:27 +0000 | [diff] [blame] | 362 | if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE ))) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 363 | return 0; |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 364 | if (((result = lseek( file->obj.fd, 0, SEEK_CUR )) == -1) || |
| 365 | (ftruncate( file->obj.fd, result ) == -1)) |
Alexandre Julliard | aa0ebd0 | 1998-12-30 12:06:45 +0000 | [diff] [blame] | 366 | { |
| 367 | file_set_error(); |
| 368 | release_object( file ); |
| 369 | return 0; |
| 370 | } |
| 371 | release_object( file ); |
| 372 | return 1; |
| 373 | |
| 374 | } |
| 375 | |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 376 | /* try to grow the file to the specified size */ |
| 377 | int grow_file( struct file *file, int size_high, int size_low ) |
| 378 | { |
| 379 | struct stat st; |
| 380 | |
| 381 | if (size_high) |
| 382 | { |
Alexandre Julliard | cb1fc73 | 2000-01-24 21:58:06 +0000 | [diff] [blame] | 383 | set_error( STATUS_INVALID_PARAMETER ); |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 384 | return 0; |
| 385 | } |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 386 | if (fstat( file->obj.fd, &st ) == -1) |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 387 | { |
| 388 | file_set_error(); |
| 389 | return 0; |
| 390 | } |
| 391 | if (st.st_size >= size_low) return 1; /* already large enough */ |
Alexandre Julliard | 1dca5e2 | 2000-01-01 00:56:27 +0000 | [diff] [blame] | 392 | if (ftruncate( file->obj.fd, size_low ) != -1) return 1; |
Alexandre Julliard | cb28bdc | 1999-02-28 10:13:59 +0000 | [diff] [blame] | 393 | file_set_error(); |
| 394 | return 0; |
| 395 | } |
| 396 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 397 | static int set_file_time( int handle, time_t access_time, time_t write_time ) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 398 | { |
| 399 | struct file *file; |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 400 | struct utimbuf utimbuf; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 401 | |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 402 | if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE ))) |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 403 | return 0; |
Alexandre Julliard | a27b48b | 1999-01-31 15:08:31 +0000 | [diff] [blame] | 404 | if (!access_time || !write_time) |
| 405 | { |
| 406 | struct stat st; |
| 407 | if (stat( file->name, &st ) == -1) goto error; |
| 408 | if (!access_time) access_time = st.st_atime; |
| 409 | if (!write_time) write_time = st.st_mtime; |
| 410 | } |
Alexandre Julliard | 0562539 | 1999-01-03 11:55:56 +0000 | [diff] [blame] | 411 | utimbuf.actime = access_time; |
| 412 | utimbuf.modtime = write_time; |
Alexandre Julliard | a27b48b | 1999-01-31 15:08:31 +0000 | [diff] [blame] | 413 | if (utime( file->name, &utimbuf ) == -1) goto error; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 414 | release_object( file ); |
| 415 | return 1; |
Alexandre Julliard | a27b48b | 1999-01-31 15:08:31 +0000 | [diff] [blame] | 416 | error: |
| 417 | file_set_error(); |
| 418 | release_object( file ); |
| 419 | return 0; |
Alexandre Julliard | 338e757 | 1998-12-27 15:28:54 +0000 | [diff] [blame] | 420 | } |
Alexandre Julliard | 62a8b43 | 1999-01-19 17:48:23 +0000 | [diff] [blame] | 421 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 422 | static int file_lock( struct file *file, int offset_high, int offset_low, |
| 423 | int count_high, int count_low ) |
Alexandre Julliard | 62a8b43 | 1999-01-19 17:48:23 +0000 | [diff] [blame] | 424 | { |
| 425 | /* FIXME: implement this */ |
| 426 | return 1; |
| 427 | } |
| 428 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 429 | static int file_unlock( struct file *file, int offset_high, int offset_low, |
| 430 | int count_high, int count_low ) |
Alexandre Julliard | 62a8b43 | 1999-01-19 17:48:23 +0000 | [diff] [blame] | 431 | { |
| 432 | /* FIXME: implement this */ |
| 433 | return 1; |
| 434 | } |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 435 | |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 436 | /* create a file */ |
| 437 | DECL_HANDLER(create_file) |
| 438 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 439 | struct file *file; |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 440 | |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 441 | req->handle = -1; |
Alexandre Julliard | 9264300 | 2000-08-31 01:59:51 +0000 | [diff] [blame] | 442 | if ((file = create_file( get_req_data(req), get_req_data_size(req), req->access, |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 443 | req->sharing, req->create, req->attrs ))) |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 444 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 445 | req->handle = alloc_handle( current->process, file, req->access, req->inherit ); |
Alexandre Julliard | 5bc7808 | 1999-06-22 17:26:53 +0000 | [diff] [blame] | 446 | release_object( file ); |
| 447 | } |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 448 | } |
| 449 | |
| 450 | /* allocate a file handle for a Unix fd */ |
| 451 | DECL_HANDLER(alloc_file_handle) |
| 452 | { |
| 453 | struct file *file; |
| 454 | |
| 455 | req->handle = -1; |
Alexandre Julliard | ea0d028 | 2000-03-10 22:16:10 +0000 | [diff] [blame] | 456 | if (current->pass_fd != -1) |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 457 | { |
Alexandre Julliard | ea0d028 | 2000-03-10 22:16:10 +0000 | [diff] [blame] | 458 | if ((file = create_file_for_fd( current->pass_fd, req->access, |
| 459 | FILE_SHARE_READ | FILE_SHARE_WRITE, 0 ))) |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 460 | { |
| 461 | req->handle = alloc_handle( current->process, file, req->access, 0 ); |
| 462 | release_object( file ); |
| 463 | } |
Alexandre Julliard | ea0d028 | 2000-03-10 22:16:10 +0000 | [diff] [blame] | 464 | current->pass_fd = -1; |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 465 | } |
Alexandre Julliard | ea0d028 | 2000-03-10 22:16:10 +0000 | [diff] [blame] | 466 | else set_error( STATUS_INVALID_PARAMETER ); |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 467 | } |
| 468 | |
Alexandre Julliard | 1ab243b | 2000-12-19 02:12:45 +0000 | [diff] [blame] | 469 | /* get a Unix fd to access a file */ |
| 470 | DECL_HANDLER(get_handle_fd) |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 471 | { |
| 472 | struct object *obj; |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 473 | |
Alexandre Julliard | 1ab243b | 2000-12-19 02:12:45 +0000 | [diff] [blame] | 474 | req->fd = -1; |
| 475 | if ((obj = get_handle_obj( current->process, req->handle, req->access, NULL ))) |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 476 | { |
Alexandre Julliard | 63411db | 2000-12-22 21:12:36 +0000 | [diff] [blame^] | 477 | int fd = get_handle_fd( current->process, req->handle, req->access ); |
| 478 | if (fd != -1) req->fd = fd; |
| 479 | else if (!get_error()) |
| 480 | { |
| 481 | if ((fd = obj->ops->get_fd( obj )) != -1) |
| 482 | send_client_fd( current, fd, req->handle ); |
| 483 | } |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 484 | release_object( obj ); |
| 485 | } |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 486 | } |
| 487 | |
| 488 | /* set a file current position */ |
| 489 | DECL_HANDLER(set_file_pointer) |
| 490 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 491 | int high = req->high; |
| 492 | int low = req->low; |
| 493 | set_file_pointer( req->handle, &low, &high, req->whence ); |
| 494 | req->new_low = low; |
| 495 | req->new_high = high; |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 496 | } |
| 497 | |
| 498 | /* truncate (or extend) a file */ |
| 499 | DECL_HANDLER(truncate_file) |
| 500 | { |
| 501 | truncate_file( req->handle ); |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 502 | } |
| 503 | |
| 504 | /* flush a file buffers */ |
| 505 | DECL_HANDLER(flush_file) |
| 506 | { |
| 507 | struct object *obj; |
| 508 | |
Marcus Meissner | 9d60e35 | 1999-12-04 04:00:16 +0000 | [diff] [blame] | 509 | if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 510 | { |
| 511 | obj->ops->flush( obj ); |
| 512 | release_object( obj ); |
| 513 | } |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 514 | } |
| 515 | |
| 516 | /* set a file access and modification times */ |
| 517 | DECL_HANDLER(set_file_time) |
| 518 | { |
| 519 | set_file_time( req->handle, req->access_time, req->write_time ); |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 520 | } |
| 521 | |
| 522 | /* get a file information */ |
| 523 | DECL_HANDLER(get_file_info) |
| 524 | { |
| 525 | struct object *obj; |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 526 | |
| 527 | if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) |
| 528 | { |
Alexandre Julliard | ebe29ef | 1999-06-26 08:43:26 +0000 | [diff] [blame] | 529 | obj->ops->get_file_info( obj, req ); |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 530 | release_object( obj ); |
| 531 | } |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 532 | } |
| 533 | |
| 534 | /* lock a region of a file */ |
| 535 | DECL_HANDLER(lock_file) |
| 536 | { |
| 537 | struct file *file; |
| 538 | |
| 539 | if ((file = get_file_obj( current->process, req->handle, 0 ))) |
| 540 | { |
| 541 | file_lock( file, req->offset_high, req->offset_low, |
| 542 | req->count_high, req->count_low ); |
| 543 | release_object( file ); |
| 544 | } |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 545 | } |
| 546 | |
| 547 | /* unlock a region of a file */ |
| 548 | DECL_HANDLER(unlock_file) |
| 549 | { |
| 550 | struct file *file; |
| 551 | |
| 552 | if ((file = get_file_obj( current->process, req->handle, 0 ))) |
| 553 | { |
| 554 | file_unlock( file, req->offset_high, req->offset_low, |
| 555 | req->count_high, req->count_low ); |
| 556 | release_object( file ); |
| 557 | } |
Alexandre Julliard | 43c190e | 1999-05-15 10:48:19 +0000 | [diff] [blame] | 558 | } |