Moved FILE_DELETE_ON_CLOSE support to the inode object so that we
really wait for the last close. Added FILE_SHARE_DELETE support.
This commit is contained in:
parent
5333206978
commit
9ff78729fa
76
server/fd.c
76
server/fd.c
|
@ -53,8 +53,9 @@
|
|||
/* closed_fd is used to keep track of the unix fd belonging to a closed fd object */
|
||||
struct closed_fd
|
||||
{
|
||||
struct closed_fd *next; /* next fd in close list */
|
||||
struct list entry; /* entry in inode closed list */
|
||||
int fd; /* the unix file descriptor */
|
||||
char unlink[1]; /* name to unlink on close (if any) */
|
||||
};
|
||||
|
||||
struct fd
|
||||
|
@ -99,7 +100,7 @@ struct inode
|
|||
ino_t ino; /* inode number */
|
||||
struct list open; /* list of open file descriptors */
|
||||
struct list locks; /* list of file locks */
|
||||
struct closed_fd *closed; /* list of file descriptors to close at destroy time */
|
||||
struct list closed; /* list of file descriptors to close at destroy time */
|
||||
};
|
||||
|
||||
static void inode_dump( struct object *obj, int verbose );
|
||||
|
@ -362,13 +363,25 @@ static struct list inode_hash[HASH_SIZE];
|
|||
/* close all pending file descriptors in the closed list */
|
||||
static void inode_close_pending( struct inode *inode )
|
||||
{
|
||||
while (inode->closed)
|
||||
struct list *ptr = list_head( &inode->closed );
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
struct closed_fd *fd = LIST_ENTRY( ptr, struct closed_fd, entry );
|
||||
struct list *next = list_next( &inode->closed, ptr );
|
||||
|
||||
if (fd->fd != -1)
|
||||
{
|
||||
struct closed_fd *fd = inode->closed;
|
||||
inode->closed = fd->next;
|
||||
close( fd->fd );
|
||||
fd->fd = -1;
|
||||
}
|
||||
if (!fd->unlink) /* get rid of it unless there's an unlink pending on that file */
|
||||
{
|
||||
list_remove( ptr );
|
||||
free( fd );
|
||||
}
|
||||
ptr = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -385,12 +398,27 @@ static void inode_dump( struct object *obj, int verbose )
|
|||
static void inode_destroy( struct object *obj )
|
||||
{
|
||||
struct inode *inode = (struct inode *)obj;
|
||||
struct list *ptr;
|
||||
|
||||
assert( list_empty(&inode->open) );
|
||||
assert( list_empty(&inode->locks) );
|
||||
|
||||
list_remove( &inode->entry );
|
||||
inode_close_pending( inode );
|
||||
|
||||
while ((ptr = list_head( &inode->closed )))
|
||||
{
|
||||
struct closed_fd *fd = LIST_ENTRY( ptr, struct closed_fd, entry );
|
||||
list_remove( ptr );
|
||||
if (fd->fd != -1) close( fd->fd );
|
||||
if (fd->unlink[0])
|
||||
{
|
||||
/* make sure it is still the same file */
|
||||
struct stat st;
|
||||
if (!stat( fd->unlink, &st ) && st.st_dev == inode->dev && st.st_ino == inode->ino)
|
||||
unlink( fd->unlink );
|
||||
}
|
||||
free( fd );
|
||||
}
|
||||
}
|
||||
|
||||
/* retrieve the inode object for a given fd, creating it if needed */
|
||||
|
@ -417,9 +445,9 @@ static struct inode *get_inode( dev_t dev, ino_t ino )
|
|||
inode->hash = hash;
|
||||
inode->dev = dev;
|
||||
inode->ino = ino;
|
||||
inode->closed = NULL;
|
||||
list_init( &inode->open );
|
||||
list_init( &inode->locks );
|
||||
list_init( &inode->closed );
|
||||
list_add_head( &inode_hash[hash], &inode->entry );
|
||||
}
|
||||
return inode;
|
||||
|
@ -430,10 +458,15 @@ static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd )
|
|||
{
|
||||
if (!list_empty( &inode->locks ))
|
||||
{
|
||||
fd->next = inode->closed;
|
||||
inode->closed = fd;
|
||||
list_add_head( &inode->closed, &fd->entry );
|
||||
}
|
||||
else /* no locks on this inode, we can close the fd right away */
|
||||
else if (fd->unlink[0]) /* close the fd but keep the structure around for unlink */
|
||||
{
|
||||
close( fd->fd );
|
||||
fd->fd = -1;
|
||||
list_add_head( &inode->closed, &fd->entry );
|
||||
}
|
||||
else /* no locks on this inode and no unlink, get rid of the fd */
|
||||
{
|
||||
close( fd->fd );
|
||||
free( fd );
|
||||
|
@ -769,7 +802,8 @@ void unlock_fd( struct fd *fd, file_pos_t start, file_pos_t count )
|
|||
static void fd_dump( struct object *obj, int verbose )
|
||||
{
|
||||
struct fd *fd = (struct fd *)obj;
|
||||
fprintf( stderr, "Fd unix_fd=%d user=%p\n", fd->unix_fd, fd->user );
|
||||
fprintf( stderr, "Fd unix_fd=%d user=%p unlink='%s'\n",
|
||||
fd->unix_fd, fd->user, fd->closed->unlink );
|
||||
}
|
||||
|
||||
static void fd_destroy( struct object *obj )
|
||||
|
@ -839,12 +873,13 @@ struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user )
|
|||
/* the sharing mode of other opens of the same file */
|
||||
static int check_sharing( struct fd *fd, unsigned int access, unsigned int sharing )
|
||||
{
|
||||
unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
unsigned int existing_access = 0;
|
||||
int unlink = 0;
|
||||
struct list *ptr;
|
||||
|
||||
/* if access mode is 0, sharing mode is ignored */
|
||||
if (!access) sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
|
||||
if (!access) sharing = existing_sharing;
|
||||
fd->access = access;
|
||||
fd->sharing = sharing;
|
||||
|
||||
|
@ -855,6 +890,7 @@ static int check_sharing( struct fd *fd, unsigned int access, unsigned int shari
|
|||
{
|
||||
existing_sharing &= fd_ptr->sharing;
|
||||
existing_access |= fd_ptr->access;
|
||||
if (fd_ptr->closed->unlink[0]) unlink = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -862,6 +898,8 @@ static int check_sharing( struct fd *fd, unsigned int access, unsigned int shari
|
|||
if ((access & GENERIC_WRITE) && !(existing_sharing & FILE_SHARE_WRITE)) return 0;
|
||||
if ((existing_access & GENERIC_READ) && !(sharing & FILE_SHARE_READ)) return 0;
|
||||
if ((existing_access & GENERIC_WRITE) && !(sharing & FILE_SHARE_WRITE)) return 0;
|
||||
if (fd->closed->unlink[0] && !(existing_sharing & FILE_SHARE_DELETE)) return 0;
|
||||
if (unlink && !(sharing & FILE_SHARE_DELETE)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -869,14 +907,14 @@ static int check_sharing( struct fd *fd, unsigned int access, unsigned int shari
|
|||
/* the fd must have been created with alloc_fd */
|
||||
/* on error the fd object is released */
|
||||
struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
|
||||
unsigned int access, unsigned int sharing )
|
||||
unsigned int access, unsigned int sharing, const char *unlink_name )
|
||||
{
|
||||
struct stat st;
|
||||
struct closed_fd *closed_fd;
|
||||
|
||||
assert( fd->unix_fd == -1 );
|
||||
|
||||
if (!(closed_fd = mem_alloc( sizeof(*closed_fd) )))
|
||||
if (!(closed_fd = mem_alloc( sizeof(*closed_fd) + strlen(unlink_name) )))
|
||||
{
|
||||
release_object( fd );
|
||||
return NULL;
|
||||
|
@ -889,6 +927,7 @@ struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
|
|||
return NULL;
|
||||
}
|
||||
closed_fd->fd = fd->unix_fd;
|
||||
closed_fd->unlink[0] = 0;
|
||||
fstat( fd->unix_fd, &st );
|
||||
*mode = st.st_mode;
|
||||
|
||||
|
@ -914,10 +953,17 @@ struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
|
|||
set_error( STATUS_SHARING_VIOLATION );
|
||||
return NULL;
|
||||
}
|
||||
strcpy( closed_fd->unlink, unlink_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
free( closed_fd );
|
||||
if (unlink_name[0]) /* we can't unlink special files */
|
||||
{
|
||||
release_object( fd );
|
||||
set_error( STATUS_INVALID_PARAMETER );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ struct file
|
|||
{
|
||||
struct object obj; /* object header */
|
||||
struct fd *fd; /* file descriptor for this file */
|
||||
char *name; /* file name */
|
||||
unsigned int access; /* file access (GENERIC_READ/WRITE) */
|
||||
unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
|
||||
int removable; /* is file on removable media? */
|
||||
|
@ -108,7 +107,6 @@ static struct file *create_file_for_fd( int fd, unsigned int access, unsigned in
|
|||
|
||||
if ((file = alloc_object( &file_ops )))
|
||||
{
|
||||
file->name = NULL;
|
||||
file->access = access;
|
||||
file->options = FILE_SYNCHRONOUS_IO_NONALERT;
|
||||
file->removable = 0;
|
||||
|
@ -163,7 +161,6 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
|||
file->access = access;
|
||||
file->options = options;
|
||||
file->removable = removable;
|
||||
file->name = name;
|
||||
if (is_overlapped( file ))
|
||||
{
|
||||
init_async_queue (&file->read_q);
|
||||
|
@ -173,11 +170,15 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
|||
/* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
|
||||
if (!(file->fd = alloc_fd( &file_fd_ops, &file->obj )) ||
|
||||
!(file->fd = open_fd( file->fd, name, flags | O_NONBLOCK | O_LARGEFILE,
|
||||
&mode, access, sharing)))
|
||||
&mode, access, sharing,
|
||||
(options & FILE_DELETE_ON_CLOSE) ? name : "" )))
|
||||
{
|
||||
free( name );
|
||||
release_object( file );
|
||||
return NULL;
|
||||
}
|
||||
free( name );
|
||||
|
||||
/* refuse to open a directory */
|
||||
if (S_ISDIR(mode) && !(options & FILE_OPEN_FOR_BACKUP_INTENT))
|
||||
{
|
||||
|
@ -233,7 +234,7 @@ static void file_dump( struct object *obj, int verbose )
|
|||
{
|
||||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
fprintf( stderr, "File fd=%p options=%08x name='%s'\n", file->fd, file->options, file->name );
|
||||
fprintf( stderr, "File fd=%p options=%08x\n", file->fd, file->options );
|
||||
}
|
||||
|
||||
static int file_get_poll_events( struct fd *fd )
|
||||
|
@ -386,11 +387,6 @@ static void file_destroy( struct object *obj )
|
|||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
|
||||
if (file->name)
|
||||
{
|
||||
if (file->options & FILE_DELETE_ON_CLOSE) unlink( file->name );
|
||||
free( file->name );
|
||||
}
|
||||
if (is_overlapped( file ))
|
||||
{
|
||||
destroy_async_queue (&file->read_q);
|
||||
|
|
|
@ -46,7 +46,7 @@ struct fd_ops
|
|||
|
||||
extern struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user );
|
||||
extern struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
|
||||
unsigned int access, unsigned int sharing );
|
||||
unsigned int access, unsigned int sharing, const char *unlink_name );
|
||||
extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops,
|
||||
int unix_fd, struct object *user );
|
||||
extern void *get_fd_user( struct fd *fd );
|
||||
|
|
Loading…
Reference in New Issue