📄 open.c
字号:
/* Unix SMB/CIFS implementation. file opening and share modes Copyright (C) Andrew Tridgell 1992-1998 Copyright (C) Jeremy Allison 2001-2004 Copyright (C) Volker Lendecke 2005 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 "includes.h"extern struct current_user current_user;extern userdom_struct current_user_info;extern uint16 global_smbpid;extern BOOL global_client_failed_oplock_break;struct deferred_open_record { BOOL delayed_for_oplocks; SMB_DEV_T dev; SMB_INO_T inode;};/**************************************************************************** fd support routines - attempt to do a dos_open.****************************************************************************/static int fd_open(struct connection_struct *conn, const char *fname, int flags, mode_t mode){ int fd;#ifdef O_NOFOLLOW if (!lp_symlinks(SNUM(conn))) { flags |= O_NOFOLLOW; }#endif fd = SMB_VFS_OPEN(conn,fname,flags,mode); DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n", fname, flags, (int)mode, fd, (fd == -1) ? strerror(errno) : "" )); return fd;}/**************************************************************************** Close the file associated with a fsp.****************************************************************************/int fd_close(struct connection_struct *conn, files_struct *fsp){ if (fsp->fh->fd == -1) { return 0; /* What we used to call a stat open. */ } if (fsp->fh->ref_count > 1) { return 0; /* Shared handle. Only close last reference. */ } return fd_close_posix(conn, fsp);}/**************************************************************************** Change the ownership of a file to that of the parent directory. Do this by fd if possible.****************************************************************************/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; int ret; ret = SMB_VFS_STAT(conn, parent_path, &parent_st); if (ret == -1) { DEBUG(0,("change_owner_to_parent: failed to stat parent " "directory %s. Error was %s\n", parent_path, strerror(errno) )); return; } if (fsp && fsp->fh->fd != -1) { become_root(); ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, parent_st.st_uid, (gid_t)-1); unbecome_root(); if (ret == -1) { DEBUG(0,("change_owner_to_parent: failed to fchown " "file %s to parent directory uid %u. Error " "was %s\n", fname, (unsigned int)parent_st.st_uid, strerror(errno) )); } DEBUG(10,("change_owner_to_parent: changed new file %s to " "parent directory uid %u.\n", fname, (unsigned int)parent_st.st_uid )); } else { /* We've already done an lstat into psbuf, and we know it's a directory. If we can cd into the directory and the dev/ino are the same then we can safely chown without races as we're locking the directory in place by being in it. This should work on any UNIX (thanks tridge :-). JRA. */ pstring saved_dir; SMB_STRUCT_STAT sbuf; if (!vfs_GetWd(conn,saved_dir)) { DEBUG(0,("change_owner_to_parent: failed to get " "current working directory\n")); return; } /* Chdir into the new path. */ if (vfs_ChDir(conn, fname) == -1) { DEBUG(0,("change_owner_to_parent: failed to change " "current working directory to %s. Error " "was %s\n", fname, strerror(errno) )); goto out; } if (SMB_VFS_STAT(conn,".",&sbuf) == -1) { DEBUG(0,("change_owner_to_parent: failed to stat " "directory '.' (%s) Error was %s\n", fname, strerror(errno))); goto out; } /* Ensure we're pointing at the same place. */ if (sbuf.st_dev != psbuf->st_dev || sbuf.st_ino != psbuf->st_ino || sbuf.st_mode != psbuf->st_mode ) { DEBUG(0,("change_owner_to_parent: " "device/inode/mode on directory %s changed. " "Refusing to chown !\n", fname )); goto out; } become_root(); ret = SMB_VFS_CHOWN(conn, ".", parent_st.st_uid, (gid_t)-1); unbecome_root(); if (ret == -1) { DEBUG(10,("change_owner_to_parent: failed to chown " "directory %s to parent directory uid %u. " "Error was %s\n", fname, (unsigned int)parent_st.st_uid, strerror(errno) )); goto out; } DEBUG(10,("change_owner_to_parent: changed ownership of new " "directory %s to parent directory uid %u.\n", fname, (unsigned int)parent_st.st_uid )); out: vfs_ChDir(conn,saved_dir); }}/**************************************************************************** Open a file.****************************************************************************/static BOOL open_file(files_struct *fsp, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, int flags, mode_t unx_mode, uint32 access_mask){ int accmode = (flags & O_ACCMODE); int local_flags = flags; BOOL file_existed = VALID_STAT(*psbuf); fsp->fh->fd = -1; errno = EPERM; /* Check permissions */ /* * This code was changed after seeing a client open request * containing the open mode of (DENY_WRITE/read-only) with * the 'create if not exist' bit set. The previous code * would fail to open the file read only on a read-only share * as it was checking the flags parameter directly against O_RDONLY, * this was failing as the flags parameter was set to O_RDONLY|O_CREAT. * JRA. */ if (!CAN_WRITE(conn)) { /* It's a read-only share - fail if we wanted to write. */ if(accmode != O_RDONLY) { DEBUG(3,("Permission denied opening %s\n",fname)); return False; } else if(flags & O_CREAT) { /* We don't want to write - but we must make sure that O_CREAT doesn't create the file if we have write access into the directory. */ flags &= ~O_CREAT; local_flags &= ~O_CREAT; } } /* * This little piece of insanity is inspired by the * fact that an NT client can open a file for O_RDONLY, * but set the create disposition to FILE_EXISTS_TRUNCATE. * If the client *can* write to the file, then it expects to * truncate the file, even though it is opening for readonly. * Quicken uses this stupid trick in backup file creation... * Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net> * for helping track this one down. It didn't bite us in 2.0.x * as we always opened files read-write in that release. JRA. */ if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) { DEBUG(10,("open_file: truncate requested on read-only open " "for file %s\n",fname )); local_flags = (flags & ~O_ACCMODE)|O_RDWR; } if ((access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || (local_flags & O_CREAT) || ((local_flags & O_TRUNC) == O_TRUNC) ) { /* * We can't actually truncate here as the file may be locked. * open_file_shared will take care of the truncate later. JRA. */ local_flags &= ~O_TRUNC;#if defined(O_NONBLOCK) && defined(S_ISFIFO) /* * We would block on opening a FIFO with no one else on the * other end. Do what we used to do and add O_NONBLOCK to the * open flags. JRA. */ if (file_existed && S_ISFIFO(psbuf->st_mode)) { local_flags |= O_NONBLOCK; }#endif /* Don't create files with Microsoft wildcard characters. */ if ((local_flags & O_CREAT) && !file_existed && ms_has_wild(fname)) { set_saved_ntstatus(NT_STATUS_OBJECT_NAME_INVALID); return False; } /* Actually do the open */ fsp->fh->fd = fd_open(conn, fname, local_flags, unx_mode); if (fsp->fh->fd == -1) { DEBUG(3,("Error opening file %s (%s) (local_flags=%d) " "(flags=%d)\n", fname,strerror(errno),local_flags,flags)); return False; } /* Inherit the ACL if the file was created. */ if ((local_flags & O_CREAT) && !file_existed) { inherit_access_acl(conn, fname, unx_mode); } } else { fsp->fh->fd = -1; /* What we used to call a stat open. */ } if (!file_existed) { int ret; if (fsp->fh->fd == -1) { ret = SMB_VFS_STAT(conn, fname, psbuf); } else { ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf); /* If we have an fd, this stat should succeed. */ if (ret == -1) { DEBUG(0,("Error doing fstat on open file %s " "(%s)\n", fname,strerror(errno) )); } } /* For a non-io open, this stat failing means file not found. JRA */ if (ret == -1) { fd_close(conn, fsp); return False; } } /* * POSIX allows read-only opens of directories. We don't * want to do this (we use a different code path for this) * so catch a directory open and return an EISDIR. JRA. */ if(S_ISDIR(psbuf->st_mode)) { fd_close(conn, fsp); errno = EISDIR; return False; } fsp->mode = psbuf->st_mode; fsp->inode = psbuf->st_ino; fsp->dev = psbuf->st_dev; fsp->vuid = current_user.vuid; fsp->file_pid = global_smbpid; fsp->can_lock = True; fsp->can_read = (access_mask & (FILE_READ_DATA)) ? True : False; if (!CAN_WRITE(conn)) { fsp->can_write = False; } else { fsp->can_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ? True : False; } fsp->print_file = False; fsp->modified = False; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; fsp->is_stat = False; if (conn->aio_write_behind_list && is_in_path(fname, conn->aio_write_behind_list, conn->case_sensitive)) { fsp->aio_write_behind = True; } string_set(&fsp->fsp_name,fname); fsp->wcp = NULL; /* Write cache pointer. */ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n", *current_user_info.smb_name ? current_user_info.smb_name : conn->user,fsp->fsp_name, BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), conn->num_files_open + 1)); errno = 0; return True;}/******************************************************************* Return True if the filename is one of the special executable types.********************************************************************/static BOOL is_executable(const char *fname){ if ((fname = strrchr_m(fname,'.'))) { if (strequal(fname,".com") || strequal(fname,".dll") || strequal(fname,".exe") || strequal(fname,".sym")) { return True; } } return False;}/**************************************************************************** Check if we can open a file with a share mode. Returns True if conflict, False if not.****************************************************************************/static BOOL share_conflict(struct share_mode_entry *entry, uint32 access_mask, uint32 share_access){ DEBUG(10,("share_conflict: entry->access_mask = 0x%x, " "entry->share_access = 0x%x, " "entry->private_options = 0x%x\n", (unsigned int)entry->access_mask, (unsigned int)entry->share_access, (unsigned int)entry->private_options)); DEBUG(10,("share_conflict: access_mask = 0x%x, share_access = 0x%x\n", (unsigned int)access_mask, (unsigned int)share_access)); if ((entry->access_mask & (FILE_WRITE_DATA| FILE_APPEND_DATA| FILE_READ_DATA| FILE_EXECUTE| DELETE_ACCESS)) == 0) { DEBUG(10,("share_conflict: No conflict due to " "entry->access_mask = 0x%x\n", (unsigned int)entry->access_mask )); return False; } if ((access_mask & (FILE_WRITE_DATA| FILE_APPEND_DATA| FILE_READ_DATA| FILE_EXECUTE| DELETE_ACCESS)) == 0) { DEBUG(10,("share_conflict: No conflict due to " "access_mask = 0x%x\n", (unsigned int)access_mask )); return False; }#if 1 /* JRA TEST - Superdebug. */#define CHECK_MASK(num, am, right, sa, share) \ DEBUG(10,("share_conflict: [%d] am (0x%x) & right (0x%x) = 0x%x\n", \ (unsigned int)(num), (unsigned int)(am), \ (unsigned int)(right), (unsigned int)(am)&(right) )); \ DEBUG(10,("share_conflict: [%d] sa (0x%x) & share (0x%x) = 0x%x\n", \ (unsigned int)(num), (unsigned int)(sa), \ (unsigned int)(share), (unsigned int)(sa)&(share) )); \ if (((am) & (right)) && !((sa) & (share))) { \ DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \ (unsigned int)(share) )); \ return True; \ }#else#define CHECK_MASK(num, am, right, sa, share) \ if (((am) & (right)) && !((sa) & (share))) { \ DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \ (unsigned int)(share) )); \ return True; \ }#endif CHECK_MASK(1, entry->access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA, share_access, FILE_SHARE_WRITE); CHECK_MASK(2, access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA, entry->share_access, FILE_SHARE_WRITE); CHECK_MASK(3, entry->access_mask, FILE_READ_DATA | FILE_EXECUTE, share_access, FILE_SHARE_READ); CHECK_MASK(4, access_mask, FILE_READ_DATA | FILE_EXECUTE, entry->share_access, FILE_SHARE_READ); CHECK_MASK(5, entry->access_mask, DELETE_ACCESS, share_access, FILE_SHARE_DELETE); CHECK_MASK(6, access_mask, DELETE_ACCESS, entry->share_access, FILE_SHARE_DELETE); DEBUG(10,("share_conflict: No conflict.\n")); return False;}#if defined(DEVELOPER)static void validate_my_share_entries(int num, struct share_mode_entry *share_entry){ files_struct *fsp; if (!procid_is_me(&share_entry->pid)) { return; } 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))); smb_panic(str); } if (!is_valid_share_mode_entry(share_entry)) { return; } fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id); if (!fsp) { DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) )); smb_panic("validate_my_share_entries: Cannot match a " "share entry with an open file\n"); } if (is_deferred_open_entry(share_entry) || is_unused_share_mode_entry(share_entry)) { goto panic; } if ((share_entry->op_type == NO_OPLOCK) && (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) { /* Someone has already written to it, but I haven't yet * noticed */ return; } if (((uint16)fsp->oplock_type) != share_entry->op_type) { goto panic; } return; panic: { pstring str;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -