r20340: Join vfs_MkDir to its only caller
[samba.git] / source3 / smbd / open.c
index 9bd4e36d5a8569744fe756d09f47e69c5e6b88d6..1ca793fb80a498b333efe645b9a84345ead914d4 100644 (file)
@@ -84,10 +84,10 @@ int fd_close(struct connection_struct *conn,
  Do this by fd if possible.
 ****************************************************************************/
 
-void change_owner_to_parent(connection_struct *conn,
-                           files_struct *fsp,
-                           const char *fname,
-                           SMB_STRUCT_STAT *psbuf)
+static void change_owner_to_parent(connection_struct *conn,
+                                  files_struct *fsp,
+                                  const char *fname,
+                                  SMB_STRUCT_STAT *psbuf)
 {
        const char *parent_path = parent_dirname(fname);
        SMB_STRUCT_STAT parent_st;
@@ -283,7 +283,9 @@ static NTSTATUS open_file(files_struct *fsp,
                }
 
                /* Inherit the ACL if the file was created. */
-               if ((local_flags & O_CREAT) && !file_existed) {
+               if ((local_flags & O_CREAT)
+                   && !file_existed
+                   && lp_inherit_perms(SNUM(conn))) {
                        inherit_access_acl(conn, fname, unx_mode);
                }
 
@@ -475,8 +477,8 @@ static void validate_my_share_entries(int num,
        if (is_deferred_open_entry(share_entry) &&
            !open_was_deferred(share_entry->op_mid)) {
                pstring str;
-               DEBUG(0, ("Got a deferred entry without a request: "
-                         "PANIC: %s\n", share_mode_str(num, share_entry)));
+               pstr_sprintf(str, "Got a deferred entry without a request: "
+                            "PANIC: %s\n", share_mode_str(num, share_entry));
                smb_panic(str);
        }
 
@@ -625,6 +627,8 @@ static BOOL delay_for_oplocks(struct share_mode_lock *lck,
        BOOL valid_entry = False;
        BOOL delay_it = False;
        BOOL have_level2 = False;
+       BOOL ret;
+       char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
 
        if (oplock_request & INTERNAL_OPEN_ONLY) {
                fsp->oplock_type = NO_OPLOCK;
@@ -688,34 +692,36 @@ static BOOL delay_for_oplocks(struct share_mode_lock *lck,
                fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
        }
 
-       if (delay_it) {
-               BOOL ret;
-               char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+       if (!delay_it) {
+               return False;
+       }
 
-               DEBUG(10, ("Sending break request to PID %s\n",
-                          procid_str_static(&exclusive->pid)));
-               exclusive->op_mid = get_current_mid();
+       /*
+        * Send a break message to the oplock holder and delay the open for
+        * our client.
+        */
 
-               /* Create the message. */
-               share_mode_entry_to_message(msg, exclusive);
+       DEBUG(10, ("Sending break request to PID %s\n",
+                  procid_str_static(&exclusive->pid)));
+       exclusive->op_mid = get_current_mid();
 
-               /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We don't
-                  want this set in the share mode struct pointed to by lck. */
+       /* Create the message. */
+       share_mode_entry_to_message(msg, exclusive);
 
-               if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) {
-                       SSVAL(msg,6,exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE);
-               }
+       /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We
+          don't want this set in the share mode struct pointed to by lck. */
 
-               become_root();
-               ret = message_send_pid(exclusive->pid, MSG_SMB_BREAK_REQUEST,
-                                      msg, MSG_SMB_SHARE_MODE_ENTRY_SIZE, True);
-               unbecome_root();
-               if (!ret) {
-                       DEBUG(3, ("Could not send oplock break message\n"));
-               }
+       if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) {
+               SSVAL(msg,6,exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE);
        }
 
-       return delay_it;
+       ret = message_send_pid(exclusive->pid, MSG_SMB_BREAK_REQUEST,
+                              msg, MSG_SMB_SHARE_MODE_ENTRY_SIZE, True);
+       if (!ret) {
+               DEBUG(3, ("Could not send oplock break message\n"));
+       }
+
+       return True;
 }
 
 static BOOL request_timed_out(struct timeval request_time,
@@ -779,28 +785,6 @@ static void defer_open(struct share_mode_lock *lck,
        srv_defer_sign_response(mid);
 }
 
-/****************************************************************************
- Set a kernel flock on a file for NFS interoperability.
- This requires a patch to Linux.
-****************************************************************************/
-
-static void kernel_flock(files_struct *fsp, uint32 share_mode)
-{
-#if HAVE_KERNEL_SHARE_MODES
-       int kernel_mode = 0;
-       if (share_mode == FILE_SHARE_WRITE) {
-               kernel_mode = LOCK_MAND|LOCK_WRITE;
-       } else if (share_mode == FILE_SHARE_READ) {
-               kernel_mode = LOCK_MAND|LOCK_READ;
-       } else if (share_mode == FILE_SHARE_NONE) {
-               kernel_mode = LOCK_MAND;
-       }
-       if (kernel_mode) {
-               flock(fsp->fh->fd, kernel_mode);
-       }
-#endif
-       ;
-}
 
 /****************************************************************************
  On overwrite open ensure that the attributes match.
@@ -1122,6 +1106,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
        struct share_mode_lock *lck = NULL;
        uint32 open_access_mask = access_mask;
        NTSTATUS status;
+       int ret_flock;
 
        if (conn->printer) {
                /* 
@@ -1135,7 +1120,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                DEBUG(10, ("open_file_ntcreate: printer open fname=%s\n", fname));
 
-               return print_fsp_open(conn, fname, &fsp);
+               return print_fsp_open(conn, fname, result);
        }
 
        /* We add aARCH to this as this mode is only used if the file is
@@ -1316,7 +1301,15 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
         */
 
        if (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) {
-               flags = O_RDWR;
+               /* DENY_DOS opens are always underlying read-write on the
+                  file handle, no matter what the requested access mask
+                   says. */
+               if ((create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS) ||
+                       access_mask & (FILE_READ_ATTRIBUTES|FILE_READ_DATA|FILE_READ_EA|FILE_EXECUTE)) {
+                       flags = O_RDWR;
+               } else {
+                       flags = O_WRONLY;
+               }
        } else {
                flags = O_RDONLY;
        }
@@ -1464,11 +1457,13 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                        if (flags & O_RDWR) {
                                can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA;
+                       } else if (flags & O_WRONLY) {
+                               can_access_mask = FILE_WRITE_DATA;
                        } else {
                                can_access_mask = FILE_READ_DATA;
                        }
 
-                       if (((flags & O_RDWR) && !CAN_WRITE(conn)) ||
+                       if (((can_access_mask & FILE_WRITE_DATA) && !CAN_WRITE(conn)) ||
                            !can_access_file(conn,fname,psbuf,can_access_mask)) {
                                can_access = False;
                        }
@@ -1640,9 +1635,18 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
            these only read them. Nobody but Samba can ever set a deny
            mode and we have already checked our more authoritative
            locking database for permission to set this deny mode. If
-           the kernel refuses the operations then the kernel is wrong */
+           the kernel refuses the operations then the kernel is wrong.
+          note that GPFS supports it as well - jmcd */
 
-       kernel_flock(fsp, share_access);
+       ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, fsp->fh->fd, share_access);
+       if(ret_flock == -1 ){
+
+               talloc_free(lck);
+               fd_close(conn, fsp);
+               file_free(fsp);
+               
+               return NT_STATUS_SHARING_VIOLATION;
+       }
 
        /*
         * At this point onwards, we can guarentee that the share entry
@@ -1847,6 +1851,48 @@ int close_file_fchmod(files_struct *fsp)
        return ret;
 }
 
+static NTSTATUS mkdir_internal(connection_struct *conn, const char *name)
+{
+       SMB_STRUCT_STAT sbuf;
+       int ret= -1;
+       mode_t mode;
+
+       if(!CAN_WRITE(conn)) {
+               DEBUG(5,("mkdir_internal: failing create on read-only share "
+                        "%s\n", lp_servicename(SNUM(conn))));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (!check_name(name, conn)) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       mode = unix_mode(conn, aDIR, name, True);
+
+       if ((ret=SMB_VFS_MKDIR(conn, name, mode)) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (lp_inherit_perms(SNUM(conn))) {
+               inherit_access_acl(conn, name, mode);
+       }
+
+       /*
+        * Check if high bits should have been set,
+        * then (if bits are missing): add them.
+        * Consider bits automagically set by UNIX, i.e. SGID bit from parent
+        * dir.
+        */
+       if (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)
+           && (SMB_VFS_STAT(conn, name, &sbuf) == 0)
+           && (mode & ~sbuf.st_mode)) {
+               SMB_VFS_CHMOD(conn, name,
+                             sbuf.st_mode | (mode & ~sbuf.st_mode));
+       }
+
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Open a directory from an NT SMB call.
 ****************************************************************************/
@@ -1863,7 +1909,6 @@ NTSTATUS open_directory(connection_struct *conn,
 {
        files_struct *fsp = NULL;
        BOOL dir_existed = VALID_STAT(*psbuf) ? True : False;
-       BOOL create_dir = False;
        struct share_mode_lock *lck = NULL;
        NTSTATUS status;
        int info = 0;
@@ -1884,44 +1929,47 @@ NTSTATUS open_directory(connection_struct *conn,
 
        switch( create_disposition ) {
                case FILE_OPEN:
-                       /* If directory exists open. If directory doesn't
-                        * exist error. */
-                       if (!dir_existed) {
-                               DEBUG(5,("open_directory: FILE_OPEN requested "
-                                        "for directory %s and it doesn't "
-                                        "exist.\n", fname ));
-                               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-                       }
+                       /*
+                        * Don't do anything. The check for existence is done
+                        * futher down.
+                        */
                        info = FILE_WAS_OPENED;
                        break;
 
                case FILE_CREATE:
+
                        /* If directory exists error. If directory doesn't
                         * exist create. */
-                       if (dir_existed) {
-                               DEBUG(5,("open_directory: FILE_CREATE "
-                                        "requested for directory %s and it "
-                                        "already exists.\n", fname ));
-                               if (use_nt_status()) {
-                                       return NT_STATUS_OBJECT_NAME_COLLISION;
-                               } else {
-                                       return NT_STATUS_DOS(ERRDOS,
-                                                            ERRfilexists);
-                               }
+
+                       status = mkdir_internal(conn, fname);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(2, ("open_directory: unable to create "
+                                         "%s. Error was %s\n", fname,
+                                         nt_errstr(status)));
+                               return status;
                        }
-                       create_dir = True;
+
                        info = FILE_WAS_CREATED;
                        break;
 
                case FILE_OPEN_IF:
-                       /* If directory exists open. If directory doesn't
-                        * exist create. */
-                       if (!dir_existed) {
-                               create_dir = True;
+                       /*
+                        * If directory exists open. If directory doesn't
+                        * exist create.
+                        */
+
+                       status = mkdir_internal(conn, fname);
+
+                       if (NT_STATUS_IS_OK(status)) {
                                info = FILE_WAS_CREATED;
-                       } else {
+                       }
+
+                       if (NT_STATUS_EQUAL(status,
+                                           NT_STATUS_OBJECT_NAME_COLLISION)) {
                                info = FILE_WAS_OPENED;
+                               status = NT_STATUS_OK;
                        }
+                               
                        break;
 
                case FILE_SUPERSEDE:
@@ -1934,35 +1982,17 @@ NTSTATUS open_directory(connection_struct *conn,
                        return NT_STATUS_INVALID_PARAMETER;
        }
 
-       if (create_dir) {
-               /*
-                * Try and create the directory.
-                */
-
-               /* We know bad_path is false as it's caught earlier. */
-
-               status = mkdir_internal(conn, fname, False);
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(2,("open_directory: unable to create %s. "
-                                "Error was %s\n", fname, strerror(errno) ));
-                       /* Ensure we return the correct NT status to the
-                        * client. */
-                       return status;
-               }
-
-               /* Ensure we're checking for a symlink here.... */
-               /* We don't want to get caught by a symlink racer. */
+       /* Ensure we're checking for a symlink here.... */
+       /* We don't want to get caught by a symlink racer. */
 
-               if(SMB_VFS_LSTAT(conn,fname, psbuf) != 0) {
-                       return map_nt_error_from_unix(errno);
-               }
+       if(SMB_VFS_LSTAT(conn,fname, psbuf) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
 
-               if(!S_ISDIR(psbuf->st_mode)) {
-                       DEBUG(0,("open_directory: %s is not a directory !\n",
-                                fname ));
-                       return NT_STATUS_NOT_A_DIRECTORY;
-               }
+       if(!S_ISDIR(psbuf->st_mode)) {
+               DEBUG(5,("open_directory: %s is not a directory !\n",
+                        fname ));
+               return NT_STATUS_NOT_A_DIRECTORY;
        }
 
        status = file_new(conn, &fsp);
@@ -2049,11 +2079,31 @@ NTSTATUS open_directory(connection_struct *conn,
        return NT_STATUS_OK;
 }
 
+NTSTATUS create_directory(connection_struct *conn, const char *directory)
+{
+       NTSTATUS status;
+       SMB_STRUCT_STAT sbuf;
+       files_struct *fsp;
+
+       SET_STAT_INVALID(sbuf);
+       
+       status = open_directory(conn, directory, &sbuf,
+                               FILE_READ_ATTRIBUTES, /* Just a stat open */
+                               FILE_SHARE_NONE, /* Ignored for stat opens */
+                               FILE_CREATE, 0, NULL, &fsp);
+
+       if (NT_STATUS_IS_OK(status)) {
+               close_file(fsp, NORMAL_CLOSE);
+       }
+
+       return status;
+}
+
 /****************************************************************************
  Open a pseudo-file (no locking checks - a 'stat' open).
 ****************************************************************************/
 
-NTSTATUS open_file_stat(connection_struct *conn, char *fname,
+NTSTATUS open_file_stat(connection_struct *conn, const char *fname,
                        SMB_STRUCT_STAT *psbuf, files_struct **result)
 {
        files_struct *fsp = NULL;