r3135: split the "create new" logic out from the "open existing" logic in
authorAndrew Tridgell <tridge@samba.org>
Fri, 22 Oct 2004 07:01:32 +0000 (07:01 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:02:25 +0000 (13:02 -0500)
pvfs_open, and handle the various race conditions that are inherent in
cifs on unix, so we do the best we can when the race happens.

the ntcreatex code is really starting to take shape now

source/ntvfs/common/opendb.c
source/ntvfs/posix/pvfs_open.c
source/ntvfs/posix/vfs_posix.h

index 63d3eacf8e290cf53bc1d128816b1590eb392b18..3b80145414afc7bcd3cc9a0133662bf3a031a16d 100644 (file)
@@ -117,11 +117,12 @@ static int odb_lock_destructor(void *ptr)
   get a lock on a entry in the odb. This call returns a lock handle,
   which the caller should unlock using talloc_free().
 */
-struct odb_lock *odb_lock(struct odb_context *odb, DATA_BLOB *file_key)
+struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
+                         struct odb_context *odb, DATA_BLOB *file_key)
 {
        struct odb_lock *lck;
 
-       lck = talloc_p(odb, struct odb_lock);
+       lck = talloc_p(mem_ctx, struct odb_lock);
        if (lck == NULL) {
                return NULL;
        }
index eb5b94e753337a0bb642d19e9ccd8eac8130f1f5..a16fbbe89116c115e9071255c1df316092000001 100644 (file)
@@ -183,10 +183,133 @@ static int pvfs_fd_destructor(void *p)
        return 0;
 }
 
+
+/*
+  form the lock context used for byte range locking and opendb
+  locking. Note that we must zero here to take account of
+  possible padding on some architectures
+*/
+static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
+                                TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+{
+       struct {
+               dev_t device;
+               ino_t inode;
+       } lock_context;
+       ZERO_STRUCT(lock_context);
+
+       lock_context.device = name->st.st_dev;
+       lock_context.inode = name->st.st_ino;
+
+       *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
+       if (key->data == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       return NT_STATUS_OK;
+}
+
+
+/*
+  create a new file
+*/
+static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
+                                struct smbsrv_request *req, 
+                                struct pvfs_filename *name, 
+                                union smb_open *io)
+{
+       struct pvfs_file *f;
+       NTSTATUS status;
+       int flags, fnum, fd;
+       struct odb_lock *lck;
+
+       flags = O_RDWR;
+
+       f = talloc_p(req, struct pvfs_file);
+       if (f == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
+       if (fnum == -1) {
+               return NT_STATUS_TOO_MANY_OPENED_FILES;
+       }
+
+       /* create the file */
+       fd = open(name->full_name, flags | O_CREAT | O_EXCL, 0644);
+       if (fd == -1) {
+               idr_remove(pvfs->idtree_fnum, fnum);
+               return pvfs_map_errno(pvfs, errno);
+       }
+
+       /* re-resolve the open fd */
+       status = pvfs_resolve_name_fd(pvfs, fd, name);
+       if (!NT_STATUS_IS_OK(status)) {
+               idr_remove(pvfs->idtree_fnum, fnum);
+               return status;
+       }
+
+       /* form the lock context used for byte range locking and
+          opendb locking */
+       status = pvfs_locking_key(name, f, &f->locking_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               idr_remove(pvfs->idtree_fnum, fnum);
+               return status;
+       }
+
+       /* grab a lock on the open file record */
+       lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
+       if (lck == NULL) {
+               DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+                        name->full_name));
+               /* we were supposed to do a blocking lock, so something
+                  is badly wrong! */
+               idr_remove(pvfs->idtree_fnum, fnum);
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       f->fnum = fnum;
+       f->fd = fd;
+       f->name = talloc_steal(f, name);
+       f->session = req->session;
+       f->smbpid = req->smbpid;
+       f->pvfs = pvfs;
+       f->pending_list = NULL;
+       f->lock_count = 0;
+
+       DLIST_ADD(pvfs->open_files, f);
+
+       /* setup a destructor to avoid file descriptor leaks on
+          abnormal termination */
+       talloc_set_destructor(f, pvfs_fd_destructor);
+
+       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.create_action = NTCREATEX_ACTION_CREATED;
+       io->generic.out.is_directory = 0;
+       io->generic.out.file_type = FILE_TYPE_DISK;
+
+       /* success - keep the file handle */
+       talloc_steal(pvfs, f);
+
+       /* release the opendb lock (in case a chained request
+          blocks) */
+       talloc_free(lck);
+
+       return NT_STATUS_OK;
+}
+
+
 /*
   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)
@@ -196,12 +319,14 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        struct pvfs_filename *name;
        struct pvfs_file *f;
        NTSTATUS status;
-       struct {
-               dev_t device;
-               ino_t inode;
-       } lock_context;
        int fnum;
+       struct odb_lock *lck;
 
+       /* use the generic mapping code to avoid implementing all the
+          different open calls. This won't allow openx to work
+          perfectly as the mapping code has no way of knowing if two
+          opens are on the same connection, so this will need to
+          change eventually */    
        if (io->generic.level != RAW_OPEN_GENERIC) {
                return ntvfs_map_open(req, io, ntvfs);
        }
@@ -229,7 +354,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                break;
 
        case NTCREATEX_DISP_OVERWRITE_IF:
-               flags = O_CREAT | O_TRUNC;
+               flags = O_TRUNC;
                break;
 
        case NTCREATEX_DISP_OPEN:
@@ -250,11 +375,11 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                if (name->exists) {
                        return NT_STATUS_OBJECT_NAME_COLLISION;
                }
-               flags = O_CREAT | O_EXCL;
+               flags = 0;
                break;
 
        case NTCREATEX_DISP_OPEN_IF:
-               flags = O_CREAT;
+               flags = 0;
                break;
 
        default:
@@ -263,32 +388,67 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 
        flags |= O_RDWR;
 
+       /* handle creating a new file separately */
+       if (!name->exists) {
+               status = pvfs_create_file(pvfs, req, name, io);
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+                       return status;
+               }
+
+               /* we've hit a race - the file was created during this call */
+               if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
+                       return status;
+               }
+
+               /* try re-resolving the name */
+               status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
+                                          PVFS_RESOLVE_NO_WILDCARD, &name);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               /* fall through to a normal open */
+       }
+
        f = talloc_p(req, struct pvfs_file);
        if (f == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
-       if (fnum == -1) {
-               return NT_STATUS_TOO_MANY_OPENED_FILES;
+       /* form the lock context used for byte range locking and
+          opendb locking */
+       status = pvfs_locking_key(name, f, &f->locking_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       fd = open(name->full_name, flags, 0644);
+       /* get a lock on this file before the actual open */
+       lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
+       if (lck == NULL) {
+               DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+                        name->full_name));
+               /* we were supposed to do a blocking lock, so something
+                  is badly wrong! */
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       /* do the actual open */
+       fd = open(name->full_name, flags);
        if (fd == -1) {
-               if (errno == 0) {
-                       errno = ENOENT;
-               }
-               idr_remove(pvfs->idtree_fnum, fnum);
                return pvfs_map_errno(pvfs, errno);
        }
 
        /* re-resolve the open fd */
        status = pvfs_resolve_name_fd(pvfs, fd, name);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
                return status;
        }
 
+       /* allocate a fnum */
+       fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
+       if (fnum == -1) {
+               return NT_STATUS_TOO_MANY_OPENED_FILES;
+       }
+
        f->fnum = fnum;
        f->fd = fd;
        f->name = talloc_steal(f, name);
@@ -298,18 +458,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        f->pending_list = NULL;
        f->lock_count = 0;
 
-       /* we must zero here to take account of padding */
-       ZERO_STRUCT(lock_context);
-       lock_context.device = name->st.st_dev;
-       lock_context.inode = name->st.st_ino;
-       f->locking_key = data_blob_talloc(f, &lock_context, sizeof(lock_context));
+       DLIST_ADD(pvfs->open_files, f);
 
        /* 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;
@@ -320,11 +474,16 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        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;
+       io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
+       io->generic.out.is_directory = 0;
+       io->generic.out.file_type = FILE_TYPE_DISK;
 
        /* success - keep the file handle */
        talloc_steal(pvfs, f);
 
+       /* unlock the locking database */
+       talloc_free(lck);
+
        return NT_STATUS_OK;
 }
 
@@ -349,7 +508,7 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
        }
 
        if (f->fd != -1 && 
-           close(f->fd) != 0) {
+           close(f->fd) == -1) {
                status = pvfs_map_errno(pvfs, errno);
        } else {
                status = NT_STATUS_OK;
index 5b6685b753c7579dbc40e4be5823c3f55fa43ecc..59924b0b1ba0ffa7c62677a81a5d33dc86b3d57d 100644 (file)
@@ -40,10 +40,10 @@ struct pvfs_state {
        struct odb_context *odb_context;
 
        /* an id tree mapping open search ID to a pvfs_search_state structure */
-       void *idtree_search;
+       struct idr_context *idtree_search;
 
        /* an id tree mapping open file handle -> struct pvfs_file */
-       void *idtree_fnum;
+       struct idr_context *idtree_fnum;
 };