1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source4/ntvfs/posix/pvfs_open.c

264 lines
6.2 KiB
C
Raw Normal View History

/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - open and close
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "include/includes.h"
#include "vfs_posix.h"
/*
find open file handle given fnum
*/
struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
struct smbsrv_request *req, uint16_t fnum)
{
struct pvfs_file *f;
for (f=pvfs->open_files;f;f=f->next) {
if (f->fnum == fnum) {
if (req->session != f->session) {
DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", fnum));
return NULL;
}
return f;
}
}
return NULL;
}
/*
by using a destructor we make sure that abnormal cleanup will not
leak file descriptors (assuming at least the top level pointer is freed, which
will cascade down to here)
*/
static int pvfs_fd_destructor(void *p)
{
struct pvfs_file *f = p;
if (f->fd != -1) {
close(f->fd);
f->fd = -1;
}
return 0;
}
/*
open a file
TODO: this is a temporary implementation derived from the simple backend
its purpose is to allow other tests to run
*/
NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
struct smbsrv_request *req, union smb_open *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
int fd, flags;
struct pvfs_filename *name;
struct pvfs_file *f;
NTSTATUS status;
if (io->generic.level != RAW_OPEN_GENERIC) {
return ntvfs_map_open(req, io, ntvfs);
}
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
PVFS_RESOLVE_NO_WILDCARD, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
switch (io->generic.in.open_disposition) {
case NTCREATEX_DISP_SUPERSEDE:
case NTCREATEX_DISP_OVERWRITE_IF:
flags = O_CREAT | O_TRUNC;
break;
case NTCREATEX_DISP_OPEN:
case NTCREATEX_DISP_OVERWRITE:
flags = 0;
break;
case NTCREATEX_DISP_CREATE:
flags = O_CREAT | O_EXCL;
break;
case NTCREATEX_DISP_OPEN_IF:
flags = O_CREAT;
break;
default:
flags = 0;
break;
}
flags |= O_RDWR;
/* we need to do this differently to support systems without O_DIRECTORY */
#ifndef O_DIRECTORY
#define O_DIRECTORY 0
#endif
if (name->exists &&
(io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
!(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
return NT_STATUS_NOT_A_DIRECTORY;
}
if ((name->exists && name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ||
(io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
flags = O_RDONLY | O_DIRECTORY;
if (pvfs->flags & PVFS_FLAG_READONLY) {
goto do_open;
}
switch (io->generic.in.open_disposition) {
case NTCREATEX_DISP_CREATE:
if (mkdir(name->full_name, 0755) == -1) {
return pvfs_map_errno(pvfs,errno);
}
break;
case NTCREATEX_DISP_OPEN_IF:
if (mkdir(name->full_name, 0755) == -1 && errno != EEXIST) {
return pvfs_map_errno(pvfs,errno);
}
break;
}
}
do_open:
fd = open(name->full_name, flags, 0644);
if (fd == -1) {
if (errno == 0)
errno = ENOENT;
return pvfs_map_errno(pvfs,errno);
}
f = talloc_p(pvfs, struct pvfs_file);
if (f == NULL) {
close(fd);
return NT_STATUS_NO_MEMORY;
}
/* re-resolve the open fd */
status = pvfs_resolve_name_fd(pvfs, fd, name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
f->fnum = fd;
f->fd = fd;
f->name = talloc_steal(f, name);
f->session = req->session;
f->smbpid = req->smbpid;
/* setup a destructor to avoid file descriptor leaks on
abnormal termination */
talloc_set_destructor(f, pvfs_fd_destructor);
DLIST_ADD(pvfs->open_files, f);
ZERO_STRUCT(io->generic.out);
io->generic.out.create_time = name->dos.create_time;
io->generic.out.access_time = name->dos.access_time;
io->generic.out.write_time = name->dos.write_time;
io->generic.out.change_time = name->dos.change_time;
io->generic.out.fnum = f->fnum;
io->generic.out.alloc_size = name->dos.alloc_size;
io->generic.out.size = name->st.st_size;
io->generic.out.attrib = name->dos.attrib;
io->generic.out.is_directory = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)?1:0;
return NT_STATUS_OK;
}
/*
close a file
*/
NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
struct smbsrv_request *req, union smb_close *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
NTSTATUS status;
if (io->generic.level != RAW_CLOSE_CLOSE) {
/* we need a mapping function */
return NT_STATUS_INVALID_LEVEL;
}
f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
if (close(f->fd) != 0) {
status = pvfs_map_errno(pvfs, errno);
} else {
status = NT_STATUS_OK;
}
talloc_set_destructor(f, NULL);
DLIST_REMOVE(pvfs->open_files, f);
talloc_free(f);
return status;
}
/*
logoff - close all file descriptors open by a vuid
*/
NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
struct smbsrv_request *req)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f, *next;
for (f=pvfs->open_files;f;f=next) {
next = f->next;
if (f->session == req->session) {
talloc_set_destructor(f, NULL);
DLIST_REMOVE(pvfs->open_files, f);
talloc_free(f);
}
}
return NT_STATUS_OK;
}
/*
exit - close files for the current pid
*/
NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
struct smbsrv_request *req)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f, *next;
for (f=pvfs->open_files;f;f=next) {
next = f->next;
if (f->smbpid == req->smbpid) {
talloc_set_destructor(f, NULL);
DLIST_REMOVE(pvfs->open_files, f);
talloc_free(f);
}
}
return NT_STATUS_OK;
}