From 42806f32e933cf1e6f6513ca95a0ec7cbb7c2bd9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 1 Dec 2009 17:38:24 +0100 Subject: [PATCH] server: Add support for opening files from a specified root directory. --- server/change.c | 3 +-- server/fd.c | 37 ++++++++++++++++++++++++++++++++++--- server/file.c | 31 ++++++++++++++++++++++++------- server/file.h | 5 +++-- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/server/change.c b/server/change.c index 06d9e37e2f..7ef456f4da 100644 --- a/server/change.c +++ b/server/change.c @@ -419,8 +419,7 @@ static void dir_destroy( struct object *obj ) } } -static struct dir * -get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access ) +struct dir *get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access ) { return (struct dir *)get_handle_obj( process, handle, access, &dir_ops ); } diff --git a/server/fd.c b/server/fd.c index 9d8a06f7a5..8f35812505 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1702,14 +1702,34 @@ void set_fd_user( struct fd *fd, const struct fd_ops *user_ops, struct object *u fd->user = user; } +static char *dup_fd_name( struct fd *root, const char *name ) +{ + char *ret; + + if (!root) return strdup( name ); + if (!root->unix_name) return NULL; + + /* skip . prefix */ + if (name[0] == '.' && (!name[1] || name[1] == '/')) name++; + + if ((ret = malloc( strlen(root->unix_name) + strlen(name) + 2 ))) + { + strcpy( ret, root->unix_name ); + if (name[0] && name[0] != '/') strcat( ret, "/" ); + strcat( ret, name ); + } + return ret; +} + /* open() wrapper that returns a struct fd with no fd user set */ -struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int access, +struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, unsigned int access, unsigned int sharing, unsigned int options ) { struct stat st; struct closed_fd *closed_fd; struct fd *fd; const char *unlink_name = ""; + int root_fd = -1; int rw_mode; if ((options & FILE_DELETE_ON_CLOSE) && !(access & DELETE)) @@ -1728,6 +1748,17 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce return NULL; } + if (root) + { + if ((root_fd = get_unix_fd( root )) == -1) goto error; + if (fchdir( root_fd ) == -1) + { + file_set_error(); + root_fd = -1; + goto error; + } + } + /* create the directory if needed */ if ((options & FILE_DIRECTORY_FILE) && (flags & O_CREAT)) { @@ -1749,8 +1780,7 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce } else rw_mode = O_RDONLY; - if (!(fd->unix_name = mem_alloc( strlen(name) + 1 ))) goto error; - strcpy( fd->unix_name, name ); + fd->unix_name = dup_fd_name( root, name ); if ((fd->unix_fd = open( name, rw_mode | (flags & ~O_TRUNC), *mode )) == -1) { @@ -1827,6 +1857,7 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce error: release_object( fd ); free( closed_fd ); + if (root_fd != -1) fchdir( server_dir_fd ); /* go back to the server dir */ return NULL; } diff --git a/server/file.c b/server/file.c index c09ddc5ca9..3e09ddd216 100644 --- a/server/file.c +++ b/server/file.c @@ -154,9 +154,10 @@ static struct object *create_file_obj( struct fd *fd, unsigned int access, mode_ return &file->obj; } -static struct object *create_file( const char *nameptr, data_size_t len, unsigned int access, - unsigned int sharing, int create, unsigned int options, - unsigned int attrs, const struct security_descriptor *sd ) +static struct object *create_file( struct fd *root, const char *nameptr, data_size_t len, + unsigned int access, unsigned int sharing, int create, + unsigned int options, unsigned int attrs, + const struct security_descriptor *sd ) { struct object *obj = NULL; struct fd *fd; @@ -164,6 +165,11 @@ static struct object *create_file( const char *nameptr, data_size_t len, unsigne char *name; mode_t mode; + if (!len || ((nameptr[0] == '/') ^ !root)) + { + set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); + return NULL; + } if (!(name = mem_alloc( len + 1 ))) return NULL; memcpy( name, nameptr, len ); name[len] = 0; @@ -203,7 +209,7 @@ static struct object *create_file( const char *nameptr, data_size_t len, unsigne access = generic_file_map_access( access ); /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */ - fd = open_fd( name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options ); + fd = open_fd( root, name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options ); if (!fd) goto done; if (S_ISDIR(mode)) @@ -628,6 +634,7 @@ struct file *grab_file_unless_removable( struct file *file ) DECL_HANDLER(create_file) { struct object *file; + struct fd *root_fd = NULL; const struct object_attributes *objattr = get_req_data(); const struct security_descriptor *sd; const char *name; @@ -644,19 +651,29 @@ DECL_HANDLER(create_file) return; } + if (objattr->rootdir) + { + struct dir *root; + + if (!(root = get_dir_obj( current->process, objattr->rootdir, 0 ))) return; + root_fd = get_obj_fd( (struct object *)root ); + release_object( root ); + if (!root_fd) return; + } + sd = objattr->sd_len ? (const struct security_descriptor *)(objattr + 1) : NULL; name = (const char *)get_req_data() + sizeof(*objattr) + objattr->sd_len; name_len = get_req_data_size() - sizeof(*objattr) - objattr->sd_len; reply->handle = 0; - if ((file = create_file( name, name_len, req->access, - req->sharing, req->create, req->options, - req->attrs, sd ))) + if ((file = create_file( root_fd, name, name_len, req->access, req->sharing, + req->create, req->options, req->attrs, sd ))) { reply->handle = alloc_handle( current->process, file, req->access, req->attributes ); release_object( file ); } + if (root_fd) release_object( root_fd ); } /* allocate a file handle for a Unix fd */ diff --git a/server/file.h b/server/file.h index a49fc4dca2..03cea89bd5 100644 --- a/server/file.h +++ b/server/file.h @@ -54,8 +54,8 @@ struct fd_ops extern struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *user, unsigned int options ); extern void set_no_fd_status( struct fd *fd, unsigned int status ); -extern struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int access, - unsigned int sharing, unsigned int options ); +extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, + unsigned int access, unsigned int sharing, unsigned int options ); extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, struct object *user, unsigned int options ); extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, @@ -125,6 +125,7 @@ extern mode_t sd_to_mode( const struct security_descriptor *sd, const SID *owner extern void do_change_notify( int unix_fd ); extern void sigio_callback(void); extern struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode ); +extern struct dir *get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access ); /* completion */ -- 2.32.0.93.g670b81a890