Exemplo n.º 1
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
	struct smb_file_time ft;
	NTSTATUS status;
	struct share_mode_lock *lck = NULL;

	ZERO_STRUCT(ft);

	if (!fsp->update_write_time_on_close) {
		return NT_STATUS_OK;
	}

	if (null_timespec(fsp->close_write_time)) {
		fsp->close_write_time = timespec_current();
	}

	/* Ensure we have a valid stat struct for the source. */
	status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	if (!VALID_STAT(fsp->fsp_name->st)) {
		/* if it doesn't seem to be a real file */
		return NT_STATUS_OK;
	}

	/* On close if we're changing the real file time we
	 * must update it in the open file db too. */
	(void)set_write_time(fsp->file_id, fsp->close_write_time);

	lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL);
	if (lck) {
		/* Close write times overwrite sticky write times
		   so we must replace any sticky write time here. */
		if (!null_timespec(lck->changed_write_time)) {
			(void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
		}
		TALLOC_FREE(lck);
	}

	ft.mtime = fsp->close_write_time;
	/* We must use NULL for the fsp handle here, as smb_set_file_time()
	   checks the fsp access_mask, which may not include FILE_WRITE_ATTRIBUTES.
	   As this is a close based update, we are not directly changing the
	   file attributes from a client call, but indirectly from a write. */
	status = smb_set_file_time(fsp->conn, NULL, fsp->fsp_name, &ft, false);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("update_write_time_on_close: smb_set_file_time "
			"on file %s returned %s\n",
			fsp_str_dbg(fsp),
			nt_errstr(status)));
		return status;
	}

	return status;
}
Exemplo n.º 2
0
static NTSTATUS vfs_worm_create_file(vfs_handle_struct *handle,
				     struct smb_request *req,
				     uint16_t root_dir_fid,
				     struct smb_filename *smb_fname,
				     uint32_t access_mask,
				     uint32_t share_access,
				     uint32_t create_disposition,
				     uint32_t create_options,
				     uint32_t file_attributes,
				     uint32_t oplock_request,
				     uint64_t allocation_size,
				     uint32_t private_flags,
				     struct security_descriptor *sd,
				     struct ea_list *ea_list,
				     files_struct **result,
				     int *pinfo)
{
	bool readonly = false;
	const uint32_t write_access_flags =
		FILE_WRITE_DATA | FILE_APPEND_DATA |
		FILE_WRITE_ATTRIBUTES | DELETE_ACCESS |
		WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS;
	NTSTATUS status;

	if (VALID_STAT(smb_fname->st)) {
		double age;
		age = timespec_elapsed(&smb_fname->st.st_ex_ctime);
		if (age > lp_parm_int(SNUM(handle->conn), "worm",
				      "grace_period", 3600)) {
			readonly = true;
		}
	}

	if (readonly && (access_mask & write_access_flags)) {
		return NT_STATUS_ACCESS_DENIED;
	}

	status = SMB_VFS_NEXT_CREATE_FILE(
		handle, req, root_dir_fid, smb_fname, access_mask,
		share_access, create_disposition, create_options,
		file_attributes, oplock_request, allocation_size,
		private_flags, sd, ea_list, result, pinfo);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	/*
	 * Access via MAXIMUM_ALLOWED_ACCESS?
	 */
	if (readonly && ((*result)->access_mask & write_access_flags)) {
		close_file(req, *result, NORMAL_CLOSE);
		return NT_STATUS_ACCESS_DENIED;
	}
	return NT_STATUS_OK;
}
Exemplo n.º 3
0
uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
{
	uint32 result = 0;

	DEBUG(8,("dos_mode: %s\n", path));

	if (!VALID_STAT(*sbuf)) {
		return 0;
	}

	/* First do any modifications that depend on the path name. */
	/* hide files with a name starting with a . */
	if (lp_hide_dot_files(SNUM(conn))) {
		const char *p = strrchr_m(path,'/');
		if (p) {
			p++;
		} else {
			p = path;
		}
		
		if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
			result |= aHIDDEN;
		}
	}
	
	/* Get the DOS attributes from an EA by preference. */
	if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
		result |= set_sparse_flag(sbuf);
	} else {
		result |= dos_mode_from_sbuf(conn, path, sbuf);
	}

	if (S_ISREG(sbuf->st_mode)) {
		result |= set_offline_flag(conn, path);
	}

	/* Optimization : Only call is_hidden_path if it's not already
	   hidden. */
	if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
		result |= aHIDDEN;
	}

	DEBUG(8,("dos_mode returning "));

	if (result & aHIDDEN) DEBUG(8, ("h"));
	if (result & aRONLY ) DEBUG(8, ("r"));
	if (result & aSYSTEM) DEBUG(8, ("s"));
	if (result & aDIR   ) DEBUG(8, ("d"));
	if (result & aARCH  ) DEBUG(8, ("a"));
	if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
	
	DEBUG(8,("\n"));

	return(result);
}
Exemplo n.º 4
0
static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
				struct smb_filename *smb_fname,
				files_struct **ret_fsp,
				bool *need_close)
{
	NTSTATUS status;
	files_struct *fsp;
	struct file_id file_id;

	*need_close = false;

	if (!VALID_STAT(smb_fname->st)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);

	for(fsp = file_find_di_first(conn->sconn, file_id);
			fsp;
			fsp = file_find_di_next(fsp)) {
		if (fsp->fh->fd != -1) {
			*ret_fsp = fsp;
			return NT_STATUS_OK;
		}
	}

	/* Opens an INTERNAL_OPEN_ONLY write handle. */
	status = SMB_VFS_CREATE_FILE(
		conn,                                   /* conn */
		NULL,                                   /* req */
		0,                                      /* root_dir_fid */
		smb_fname,                              /* fname */
		FILE_WRITE_DATA,                        /* access_mask */
		(FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
			FILE_SHARE_DELETE),
		FILE_OPEN,                              /* create_disposition*/
		0,                                      /* create_options */
		0,                                      /* file_attributes */
		INTERNAL_OPEN_ONLY,                     /* oplock_request */
		NULL,					/* lease */
                0,                                      /* allocation_size */
		0,                                      /* private_flags */
		NULL,                                   /* sd */
		NULL,                                   /* ea_list */
		ret_fsp,                                /* result */
		NULL,                                   /* pinfo */
		NULL, NULL);				/* create context */

	if (NT_STATUS_IS_OK(status)) {
		*need_close = true;
	}
	return status;
}
Exemplo n.º 5
0
uint32_t dos_mode_msdfs(connection_struct *conn,
		      const struct smb_filename *smb_fname)
{
	uint32_t result = 0;

	DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));

	if (!VALID_STAT(smb_fname->st)) {
		return 0;
	}

	/* First do any modifications that depend on the path name. */
	/* hide files with a name starting with a . */
	if (lp_hide_dot_files(SNUM(conn))) {
		const char *p = strrchr_m(smb_fname->base_name, '/');
		if (p) {
			p++;
		} else {
			p = smb_fname->base_name;
		}

		/* Only . and .. are not hidden. */
		if (p[0] == '.' && !((p[1] == '\0') ||
				(p[1] == '.' && p[2] == '\0'))) {
			result |= FILE_ATTRIBUTE_HIDDEN;
		}
	}

	result |= dos_mode_from_sbuf(conn, smb_fname);

	/* Optimization : Only call is_hidden_path if it's not already
	   hidden. */
	if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
	    IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
		result |= FILE_ATTRIBUTE_HIDDEN;
	}

	if (result == 0) {
		result = FILE_ATTRIBUTE_NORMAL;
	}

	result = filter_mode_by_protocol(result);

	/*
	 * Add in that it is a reparse point
	 */
	result |= FILE_ATTRIBUTE_REPARSE_POINT;

	dos_mode_debug_print(__func__, result);

	return(result);
}
Exemplo n.º 6
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
    struct smb_file_time ft;
    NTSTATUS status;
    struct share_mode_lock *lck = NULL;

    ZERO_STRUCT(ft);

    if (!fsp->update_write_time_on_close) {
        return NT_STATUS_OK;
    }

    if (null_timespec(fsp->close_write_time)) {
        fsp->close_write_time = timespec_current();
    }

    /* Ensure we have a valid stat struct for the source. */
    status = vfs_stat_fsp(fsp);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    if (!VALID_STAT(fsp->fsp_name->st)) {
        /* if it doesn't seem to be a real file */
        return NT_STATUS_OK;
    }

    /* On close if we're changing the real file time we
     * must update it in the open file db too. */
    (void)set_write_time(fsp->file_id, fsp->close_write_time);

    lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL);
    if (lck) {
        /* Close write times overwrite sticky write times
           so we must replace any sticky write time here. */
        if (!null_timespec(lck->changed_write_time)) {
            (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
        }
        TALLOC_FREE(lck);
    }

    ft.mtime = fsp->close_write_time;
    status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    return status;
}
Exemplo n.º 7
0
bool can_access_file_data(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
{
	if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) {
		return False;
	}
	access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA);

	/* some fast paths first */

	DEBUG(10,("can_access_file_data: requesting 0x%x on file %s\n",
		(unsigned int)access_mask, fname ));

	if (conn->server_info->utok.uid == 0 || conn->admin_user) {
		/* I'm sorry sir, I didn't know you were root... */
		return True;
	}

	if (!VALID_STAT(*psbuf)) {
		/* Get the file permission mask and owners. */
		if(SMB_VFS_STAT(conn, fname, psbuf) != 0) {
			return False;
		}
	}

	/* Check primary owner access. */
	if (conn->server_info->utok.uid == psbuf->st_uid) {
		switch (access_mask) {
			case FILE_READ_DATA:
				return (psbuf->st_mode & S_IRUSR) ? True : False;

			case FILE_WRITE_DATA:
				return (psbuf->st_mode & S_IWUSR) ? True : False;

			default: /* FILE_READ_DATA|FILE_WRITE_DATA */

				if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) {
					return True;
				} else {
					return False;
				}
		}
	}

	/* now for ACL checks */

	return can_access_file_acl(conn, fname, access_mask);
}
Exemplo n.º 8
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
    SMB_STRUCT_STAT sbuf;
    struct timespec ts[2];
    NTSTATUS status;
    int ret = -1;

    ZERO_STRUCT(sbuf);
    ZERO_STRUCT(ts);

    if (!fsp->update_write_time_on_close) {
        return NT_STATUS_OK;
    }

    if (null_timespec(fsp->close_write_time)) {
        fsp->close_write_time = timespec_current();
    }

    /* Ensure we have a valid stat struct for the source. */
    if (fsp->fh->fd != -1) {
        ret = SMB_VFS_FSTAT(fsp, &sbuf);
    } else {
        if (fsp->posix_open) {
            ret = SMB_VFS_LSTAT(fsp->conn,fsp->fsp_name,&sbuf);
        } else {
            ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name,&sbuf);
        }
    }

    if (ret == -1) {
        return map_nt_error_from_unix(errno);
    }

    if (!VALID_STAT(sbuf)) {
        /* if it doesn't seem to be a real file */
        return NT_STATUS_OK;
    }

    ts[1] = fsp->close_write_time;
    status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name,
                               &sbuf, ts, true);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    return NT_STATUS_OK;
}
Exemplo n.º 9
0
/*
 * Check if someone changed the POSIX mode, for files we expect 0666, for
 * directories 0777. Discard the ACL blob if the mode is different.
 */
static bool nfs4acl_validate_blob(vfs_handle_struct *handle,
				  const struct smb_filename *smb_fname)
{
	struct nfs4acl_config *config = NULL;
	mode_t expected_mode;
	int saved_errno = 0;
	int ret;

	SMB_VFS_HANDLE_GET_DATA(handle, config,
				struct nfs4acl_config,
				return false);

	if (!VALID_STAT(smb_fname->st)) {
		/* might be a create */
		return true;
	}

	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
		expected_mode = 0777;
	} else {
		expected_mode = 0666;
	}
	if ((smb_fname->st.st_ex_mode & expected_mode) == expected_mode) {
		return true;
	}

	become_root();
	ret = SMB_VFS_NEXT_REMOVEXATTR(handle,
				       smb_fname,
				       config->xattr_name);
	if (ret != 0) {
		saved_errno = errno;
	}
	unbecome_root();
	if (saved_errno != 0) {
		errno = saved_errno;
	}
	if (ret != 0 && errno != ENOATTR) {
		DBG_ERR("Removing NFS4 xattr failed: %s\n", strerror(errno));
		return false;
	}

	return true;
}
Exemplo n.º 10
0
static NTSTATUS nfs4acl_xattr_default_sd(
	struct vfs_handle_struct *handle,
	const struct smb_filename *smb_fname,
	TALLOC_CTX *mem_ctx,
	struct security_descriptor **sd)
{
	struct nfs4acl_config *config = NULL;
	enum default_acl_style default_acl_style;
	mode_t required_mode;
	SMB_STRUCT_STAT sbuf = smb_fname->st;
	int ret;

	SMB_VFS_HANDLE_GET_DATA(handle, config,
				struct nfs4acl_config,
				return NT_STATUS_INTERNAL_ERROR);

	default_acl_style = config->default_acl_style;

	if (!VALID_STAT(sbuf)) {
		ret = vfs_stat_smb_basename(handle->conn,
					    smb_fname,
					    &sbuf);
		if (ret != 0) {
			return map_nt_error_from_unix(errno);
		}
	}

	if (S_ISDIR(sbuf.st_ex_mode)) {
		required_mode = 0777;
	} else {
		required_mode = 0666;
	}
	if ((sbuf.st_ex_mode & required_mode) != required_mode) {
		default_acl_style = DEFAULT_ACL_POSIX;
	}

	return make_default_filesystem_acl(mem_ctx,
					   default_acl_style,
					   smb_fname->base_name,
					   &sbuf,
					   sd);
}
Exemplo n.º 11
0
int non_posix_sys_acl_blob_get_fd_helper(vfs_handle_struct *handle,
					 files_struct *fsp,
					 DATA_BLOB acl_as_blob,
					 TALLOC_CTX *mem_ctx,
					 DATA_BLOB *blob)
{
	SMB_STRUCT_STAT sbuf;
	TALLOC_CTX *frame;
	struct xattr_sys_acl_hash_wrapper acl_wrapper;
	int ret;

	frame = talloc_stackframe();

	acl_wrapper.acl_as_blob = acl_as_blob;

	if (!VALID_STAT(fsp->fsp_name->st)) {
		ret = smb_vfs_call_fstat(handle, fsp, &sbuf);
		if (ret == -1) {
			TALLOC_FREE(frame);
			return -1;
		}
	} else {
		sbuf = fsp->fsp_name->st;
	}

	acl_wrapper.owner = sbuf.st_ex_uid;
	acl_wrapper.group = sbuf.st_ex_gid;
	acl_wrapper.mode = sbuf.st_ex_mode;

	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(blob, mem_ctx,
							  &acl_wrapper,
							  (ndr_push_flags_fn_t)ndr_push_xattr_sys_acl_hash_wrapper))) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	TALLOC_FREE(frame);
	return 0;
}
Exemplo n.º 12
0
static bool smbacl4_fill_ace4(
    const struct smb_filename *filename,
    smbacl4_vfs_params *params,
    uid_t ownerUID,
    gid_t ownerGID,
    const struct security_ace *ace_nt, /* input */
    SMB_ACE4PROP_T *ace_v4 /* output */
)
{
    DEBUG(10, ("got ace for %s\n", sid_string_dbg(&ace_nt->trustee)));

    memset(ace_v4, 0, sizeof(SMB_ACE4PROP_T));

    /* only ACCESS|DENY supported right now */
    ace_v4->aceType = ace_nt->type;

    ace_v4->aceFlags = map_windows_ace_flags_to_nfs4_ace_flags(
                           ace_nt->flags);

    /* remove inheritance flags on files */
    if (VALID_STAT(filename->st) &&
            !S_ISDIR(filename->st.st_ex_mode)) {
        DEBUG(10, ("Removing inheritance flags from a file\n"));
        ace_v4->aceFlags &= ~(SMB_ACE4_FILE_INHERIT_ACE|
                              SMB_ACE4_DIRECTORY_INHERIT_ACE|
                              SMB_ACE4_NO_PROPAGATE_INHERIT_ACE|
                              SMB_ACE4_INHERIT_ONLY_ACE);
    }

    ace_v4->aceMask = ace_nt->access_mask &
                      (SEC_STD_ALL | SEC_FILE_ALL);

    se_map_generic(&ace_v4->aceMask, &file_generic_mapping);

    if (ace_v4->aceFlags!=ace_nt->flags)
        DEBUG(9, ("ace_v4->aceFlags(0x%x)!=ace_nt->flags(0x%x)\n",
                  ace_v4->aceFlags, ace_nt->flags));

    if (ace_v4->aceMask!=ace_nt->access_mask)
        DEBUG(9, ("ace_v4->aceMask(0x%x)!=ace_nt->access_mask(0x%x)\n",
                  ace_v4->aceMask, ace_nt->access_mask));

    if (dom_sid_equal(&ace_nt->trustee, &global_sid_World)) {
        ace_v4->who.special_id = SMB_ACE4_WHO_EVERYONE;
        ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
    } else if (params->mode!=e_special &&
               dom_sid_equal(&ace_nt->trustee,
                             &global_sid_Creator_Owner)) {
        DEBUG(10, ("Map creator owner\n"));
        ace_v4->who.special_id = SMB_ACE4_WHO_OWNER;
        ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
        /* A non inheriting creator owner entry has no effect. */
        ace_v4->aceFlags |= SMB_ACE4_INHERIT_ONLY_ACE;
        if (!(ace_v4->aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)
                && !(ace_v4->aceFlags & SMB_ACE4_FILE_INHERIT_ACE)) {
            return false;
        }
    } else if (params->mode!=e_special &&
               dom_sid_equal(&ace_nt->trustee,
                             &global_sid_Creator_Group)) {
        DEBUG(10, ("Map creator owner group\n"));
        ace_v4->who.special_id = SMB_ACE4_WHO_GROUP;
        ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
        /* A non inheriting creator group entry has no effect. */
        ace_v4->aceFlags |= SMB_ACE4_INHERIT_ONLY_ACE;
        if (!(ace_v4->aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)
                && !(ace_v4->aceFlags & SMB_ACE4_FILE_INHERIT_ACE)) {
            return false;
        }
    } else {
        uid_t uid;
        gid_t gid;

        if (sid_to_gid(&ace_nt->trustee, &gid)) {
            ace_v4->aceFlags |= SMB_ACE4_IDENTIFIER_GROUP;
            ace_v4->who.gid = gid;
        } else if (sid_to_uid(&ace_nt->trustee, &uid)) {
            ace_v4->who.uid = uid;
        } else {
            DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
                      "convert %s to uid or gid\n",
                      filename->base_name,
                      sid_string_dbg(&ace_nt->trustee)));
            return false;
        }
    }

    return true; /* OK */
}
Exemplo n.º 13
0
NTSTATUS unix_convert(TALLOC_CTX *ctx,
			connection_struct *conn,
			const char *orig_path,
			bool allow_wcard_last_component,
			char **pp_conv_path,
			char **pp_saved_last_component,
			SMB_STRUCT_STAT *pst)
{
	SMB_STRUCT_STAT st;
	char *start, *end;
	char *dirpath = NULL;
	char *name = NULL;
	char *stream = NULL;
	bool component_was_mangled = False;
	bool name_has_wildcard = False;
	bool posix_pathnames = false;
	NTSTATUS result;
	int ret = -1;

	SET_STAT_INVALID(*pst);
	*pp_conv_path = NULL;
	if(pp_saved_last_component) {
		*pp_saved_last_component = NULL;
	}

	if (conn->printer) {
		/* we don't ever use the filenames on a printer share as a
			filename - so don't convert them */
		if (!(*pp_conv_path = talloc_strdup(ctx,orig_path))) {
			return NT_STATUS_NO_MEMORY;
		}
		return NT_STATUS_OK;
	}

	DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path));

	/*
	 * Conversion to basic unix format is already done in
	 * check_path_syntax().
	 */

	/*
	 * Names must be relative to the root of the service - any leading /.
	 * and trailing /'s should have been trimmed by check_path_syntax().
	 */

#ifdef DEVELOPER
	SMB_ASSERT(*orig_path != '/');
#endif

	/*
	 * If we trimmed down to a single '\0' character
	 * then we should use the "." directory to avoid
	 * searching the cache, but not if we are in a
	 * printing share.
	 * As we know this is valid we can return true here.
	 */

	if (!*orig_path) {
		if (!(name = talloc_strdup(ctx,"."))) {
			return NT_STATUS_NO_MEMORY;
		}
		if (SMB_VFS_STAT(conn,name,&st) == 0) {
			*pst = st;
		} else {
			return map_nt_error_from_unix(errno);
		}
		DEBUG(5,("conversion finished \"\" -> %s\n",name));
		goto done;
	}

	if (orig_path[0] == '.' && (orig_path[1] == '/' ||
				orig_path[1] == '\0')) {
		/* Start of pathname can't be "." only. */
		if (orig_path[1] == '\0' || orig_path[2] == '\0') {
			result = NT_STATUS_OBJECT_NAME_INVALID;
		} else {
			result =determine_path_error(
				&orig_path[2], allow_wcard_last_component);
		}
		return result;
	}

	if (!(name = talloc_strdup(ctx, orig_path))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		return NT_STATUS_NO_MEMORY;
	}

	/*
	 * Large directory fix normalization. If we're case sensitive, and
	 * the case preserving parameters are set to "no", normalize the case of
	 * the incoming filename from the client WHETHER IT EXISTS OR NOT !
	 * This is in conflict with the current (3.0.20) man page, but is
	 * what people expect from the "large directory howto". I'll update
	 * the man page. Thanks to [email protected] for finding this. JRA.
	 */

	if (conn->case_sensitive && !conn->case_preserve &&
			!conn->short_case_preserve) {
		strnorm(name, lp_defaultcase(SNUM(conn)));
	}

	/*
	 * Ensure saved_last_component is valid even if file exists.
	 */

	if(pp_saved_last_component) {
		end = strrchr_m(name, '/');
		if (end) {
			*pp_saved_last_component = talloc_strdup(ctx, end + 1);
		} else {
			*pp_saved_last_component = talloc_strdup(ctx,
							name);
		}
	}

	posix_pathnames = lp_posix_pathnames();

	if (!posix_pathnames) {
		stream = strchr_m(name, ':');

		if (stream != NULL) {
			char *tmp = talloc_strdup(ctx, stream);
			if (tmp == NULL) {
				TALLOC_FREE(name);
				return NT_STATUS_NO_MEMORY;
			}
			*stream = '\0';
			stream = tmp;
		}
	}

	start = name;

	/* If we're providing case insentive semantics or
	 * the underlying filesystem is case insensitive,
	 * then a case-normalized hit in the stat-cache is
	 * authoratitive. JRA.
	 */

	if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) &&
			stat_cache_lookup(conn, &name, &dirpath, &start, &st)) {
		*pst = st;
		goto done;
	}

	/*
	 * Make sure "dirpath" is an allocated string, we use this for
	 * building the directories with asprintf and free it.
	 */

	if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		TALLOC_FREE(name);
		return NT_STATUS_NO_MEMORY;
	}

	/*
	 * stat the name - if it exists then we are all done!
	 */

	if (posix_pathnames) {
		ret = SMB_VFS_LSTAT(conn,name,&st);
	} else {
		ret = SMB_VFS_STAT(conn,name,&st);
	}

	if (ret == 0) {
		/* Ensure we catch all names with in "/."
		   this is disallowed under Windows. */
		const char *p = strstr(name, "/."); /* mb safe. */
		if (p) {
			if (p[2] == '/') {
				/* Error code within a pathname. */
				result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			} else if (p[2] == '\0') {
				/* Error code at the end of a pathname. */
				result = NT_STATUS_OBJECT_NAME_INVALID;
				goto fail;
			}
		}
		stat_cache_add(orig_path, name, conn->case_sensitive);
		DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
		*pst = st;
		goto done;
	}

	DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n",
				name, dirpath, start));

	/*
	 * A special case - if we don't have any mangling chars and are case
	 * sensitive or the underlying filesystem is case insentive then searching
	 * won't help.
	 */

	if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) &&
			!mangle_is_mangled(name, conn->params)) {
		goto done;
	}

	/*
	 * is_mangled() was changed to look at an entire pathname, not
	 * just a component. JRA.
	 */

	if (mangle_is_mangled(start, conn->params)) {
		component_was_mangled = True;
	}

	/*
	 * Now we need to recursively match the name against the real
	 * directory structure.
	 */

	/*
	 * Match each part of the path name separately, trying the names
	 * as is first, then trying to scan the directory for matching names.
	 */

	for (; start ; start = (end?end+1:(char *)NULL)) {
		/*
		 * Pinpoint the end of this section of the filename.
		 */
		/* mb safe. '/' can't be in any encoded char. */
		end = strchr(start, '/');

		/*
		 * Chop the name at this point.
		 */
		if (end) {
			*end = 0;
		}

		if (pp_saved_last_component) {
			TALLOC_FREE(*pp_saved_last_component);
			*pp_saved_last_component = talloc_strdup(ctx,
							end ? end + 1 : start);
			if (!*pp_saved_last_component) {
				DEBUG(0, ("talloc failed\n"));
				return NT_STATUS_NO_MEMORY;
			}
		}

		/* The name cannot have a component of "." */

		if (ISDOT(start)) {
			if (!end)  {
				/* Error code at the end of a pathname. */
				result = NT_STATUS_OBJECT_NAME_INVALID;
			} else {
				result = determine_path_error(end+1,
						allow_wcard_last_component);
			}
			goto fail;
		}

		/* The name cannot have a wildcard if it's not
		   the last component. */

		name_has_wildcard = ms_has_wild(start);

		/* Wildcard not valid anywhere. */
		if (name_has_wildcard && !allow_wcard_last_component) {
			result = NT_STATUS_OBJECT_NAME_INVALID;
			goto fail;
		}

		/* Wildcards never valid within a pathname. */
		if (name_has_wildcard && end) {
			result = NT_STATUS_OBJECT_NAME_INVALID;
			goto fail;
		}

		/*
		 * Check if the name exists up to this point.
		 */

		if (posix_pathnames) {
			ret = SMB_VFS_LSTAT(conn,name, &st);
		} else {
			ret = SMB_VFS_STAT(conn,name, &st);
		}

		if (ret == 0) {
			/*
			 * It exists. it must either be a directory or this must
			 * be the last part of the path for it to be OK.
			 */
			if (end && !(st.st_mode & S_IFDIR)) {
				/*
				 * An intermediate part of the name isn't
				 * a directory.
				 */
				DEBUG(5,("Not a dir %s\n",start));
				*end = '/';
				/*
				 * We need to return the fact that the
				 * intermediate name resolution failed. This
				 * is used to return an error of ERRbadpath
				 * rather than ERRbadfile. Some Windows
				 * applications depend on the difference between
				 * these two errors.
				 */
				result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			}

			if (!end) {
				/*
				 * We just scanned for, and found the end of
				 * the path. We must return the valid stat
				 * struct. JRA.
				 */

				*pst = st;
			}

		} else {
			char *found_name = NULL;

			/* Stat failed - ensure we don't use it. */
			SET_STAT_INVALID(st);

			/*
			 * Reset errno so we can detect
			 * directory open errors.
			 */
			errno = 0;

			/*
			 * Try to find this part of the path in the directory.
			 */

			if (name_has_wildcard ||
			    (get_real_filename_mangled(
				     conn, dirpath, start,
				     talloc_tos(), &found_name) == -1)) {
				char *unmangled;

				if (end) {
					/*
					 * An intermediate part of the name
					 * can't be found.
					 */
					DEBUG(5,("Intermediate not found %s\n",
							start));
					*end = '/';

					/*
					 * We need to return the fact that the
					 * intermediate name resolution failed.
					 * This is used to return an error of
					 * ERRbadpath rather than ERRbadfile.
					 * Some Windows applications depend on
					 * the difference between these two
					 * errors.
					 */

					/*
					 * ENOENT, ENOTDIR and ELOOP all map
					 * to NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */

					if (errno == ENOENT ||
							errno == ENOTDIR ||
							errno == ELOOP) {
						result =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					else {
						result =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/* ENOENT is the only valid error here. */
				if ((errno != 0) && (errno != ENOENT)) {
					/*
					 * ENOTDIR and ELOOP both map to
					 * NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */
					if (errno == ENOTDIR ||
							errno == ELOOP) {
						result =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					else {
						result =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/*
				 * Just the last part of the name doesn't exist.
				 * We need to strupper() or strlower() it as
				 * this conversion may be used for file creation
				 * purposes. Fix inspired by
				 * Thomas Neumann <*****@*****.**>.
				 */
				if (!conn->case_preserve ||
				    (mangle_is_8_3(start, False,
						   conn->params) &&
						 !conn->short_case_preserve)) {
					strnorm(start,
						lp_defaultcase(SNUM(conn)));
				}

				/*
				 * check on the mangled stack to see if we can
				 * recover the base of the filename.
				 */

				if (mangle_is_mangled(start, conn->params)
				    && mangle_lookup_name_from_8_3(ctx,
					    		start,
							&unmangled,
							conn->params)) {
					char *tmp;
					size_t start_ofs = start - name;

					if (*dirpath != '\0') {
						tmp = talloc_asprintf(ctx,
							"%s/%s", dirpath,
							unmangled);
						TALLOC_FREE(unmangled);
					}
					else {
						tmp = unmangled;
					}
					if (tmp == NULL) {
						DEBUG(0, ("talloc failed\n"));
						return NT_STATUS_NO_MEMORY;
					}
					TALLOC_FREE(name);
					name = tmp;
					start = name + start_ofs;
					end = start + strlen(start);
				}

				DEBUG(5,("New file %s\n",start));
				goto done;
			}


			/*
			 * Restore the rest of the string. If the string was
			 * mangled the size may have changed.
			 */
			if (end) {
				char *tmp;
				size_t start_ofs = start - name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(ctx,
						"%s/%s/%s", dirpath,
						found_name, end+1);
				}
				else {
					tmp = talloc_asprintf(ctx,
						"%s/%s", found_name,
						end+1);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc_asprintf failed\n"));
					return NT_STATUS_NO_MEMORY;
				}
				TALLOC_FREE(name);
				name = tmp;
				start = name + start_ofs;
				end = start + strlen(found_name);
				*end = '\0';
			} else {
				char *tmp;
				size_t start_ofs = start - name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(ctx,
						"%s/%s", dirpath,
						found_name);
				} else {
					tmp = talloc_strdup(ctx,
						found_name);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc failed\n"));
					return NT_STATUS_NO_MEMORY;
				}
				TALLOC_FREE(name);
				name = tmp;
				start = name + start_ofs;

				/*
				 * We just scanned for, and found the end of
				 * the path. We must return a valid stat struct
				 * if it exists. JRA.
				 */

				if (posix_pathnames) {
					ret = SMB_VFS_LSTAT(conn,name, &st);
				} else {
					ret = SMB_VFS_STAT(conn,name, &st);
				}

				if (ret == 0) {
					*pst = st;
				} else {
					SET_STAT_INVALID(st);
				}
			}

			TALLOC_FREE(found_name);
		} /* end else */

#ifdef DEVELOPER
		/*
		 * This sucks!
		 * We should never provide different behaviors
		 * depending on DEVELOPER!!!
		 */
		if (VALID_STAT(st)) {
			bool delete_pending;
			get_file_infos(vfs_file_id_from_sbuf(conn, &st),
				       &delete_pending, NULL);
			if (delete_pending) {
				result = NT_STATUS_DELETE_PENDING;
				goto fail;
			}
		}
#endif

		/*
		 * Add to the dirpath that we have resolved so far.
		 */

		if (*dirpath != '\0') {
			char *tmp = talloc_asprintf(ctx,
					"%s/%s", dirpath, start);
			if (!tmp) {
				DEBUG(0, ("talloc_asprintf failed\n"));
				return NT_STATUS_NO_MEMORY;
			}
			TALLOC_FREE(dirpath);
			dirpath = tmp;
		}
		else {
			TALLOC_FREE(dirpath);
			if (!(dirpath = talloc_strdup(ctx,start))) {
				DEBUG(0, ("talloc_strdup failed\n"));
				return NT_STATUS_NO_MEMORY;
			}
		}

		/*
		 * Don't cache a name with mangled or wildcard components
		 * as this can change the size.
		 */

		if(!component_was_mangled && !name_has_wildcard) {
			stat_cache_add(orig_path, dirpath,
					conn->case_sensitive);
		}

		/*
		 * Restore the / that we wiped out earlier.
		 */
		if (end) {
			*end = '/';
		}
	}

	/*
	 * Don't cache a name with mangled or wildcard components
	 * as this can change the size.
	 */

	if(!component_was_mangled && !name_has_wildcard) {
		stat_cache_add(orig_path, name, conn->case_sensitive);
	}

	/*
	 * The name has been resolved.
	 */

	DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));

 done:
	if (stream != NULL) {
		char *tmp = NULL;

		result = build_stream_path(ctx, conn, orig_path, name, stream,
					   pst, &tmp);
		if (!NT_STATUS_IS_OK(result)) {
			goto fail;
		}

		DEBUG(10, ("build_stream_path returned %s\n", tmp));

		TALLOC_FREE(name);
		name = tmp;
	}
	*pp_conv_path = name;
	TALLOC_FREE(dirpath);
	return NT_STATUS_OK;
 fail:
	DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start));
	if (*dirpath != '\0') {
		*pp_conv_path = talloc_asprintf(ctx,
				"%s/%s", dirpath, start);
	} else {
		*pp_conv_path = talloc_strdup(ctx, start);
	}
	if (!*pp_conv_path) {
		DEBUG(0, ("talloc_asprintf failed\n"));
		return NT_STATUS_NO_MEMORY;
	}
	TALLOC_FREE(name);
	TALLOC_FREE(dirpath);
	return result;
}
Exemplo n.º 14
0
int file_set_dosmode(connection_struct *conn, const char *fname,
		     uint32 dosmode, SMB_STRUCT_STAT *st,
		     const char *parent_dir)
{
	SMB_STRUCT_STAT st1;
	int mask=0;
	mode_t tmp;
	mode_t unixmode;
	int ret = -1;

	/* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
	dosmode &= SAMBA_ATTRIBUTES_MASK;

	DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));

	if (st == NULL) {
		SET_STAT_INVALID(st1);
		st = &st1;
	}

	if (!VALID_STAT(*st)) {
		if (SMB_VFS_STAT(conn,fname,st))
			return(-1);
	}

	unixmode = st->st_mode;

	get_acl_group_bits(conn, fname, &st->st_mode);

	if (S_ISDIR(st->st_mode))
		dosmode |= aDIR;
	else
		dosmode &= ~aDIR;

	if (dos_mode(conn,fname,st) == dosmode) {
		st->st_mode = unixmode;
		return(0);
	}

	/* Store the DOS attributes in an EA by preference. */
	if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
		st->st_mode = unixmode;
		return 0;
	}

	unixmode = unix_mode(conn,dosmode,fname, parent_dir);

	/* preserve the s bits */
	mask |= (S_ISUID | S_ISGID);

	/* preserve the t bit */
#ifdef S_ISVTX
	mask |= S_ISVTX;
#endif

	/* possibly preserve the x bits */
	if (!MAP_ARCHIVE(conn))
		mask |= S_IXUSR;
	if (!MAP_SYSTEM(conn))
		mask |= S_IXGRP;
	if (!MAP_HIDDEN(conn))
		mask |= S_IXOTH;

	unixmode |= (st->st_mode & mask);

	/* if we previously had any r bits set then leave them alone */
	if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
		unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
		unixmode |= tmp;
	}

	/* if we previously had any w bits set then leave them alone 
		whilst adding in the new w bits, if the new mode is not rdonly */
	if (!IS_DOS_READONLY(dosmode)) {
		unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
	}

	if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
		notify_fname(conn, NOTIFY_ACTION_MODIFIED,
			     FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
		st->st_mode = unixmode;
		return 0;
	}

	if((errno != EPERM) && (errno != EACCES))
		return -1;

	if(!lp_dos_filemode(SNUM(conn)))
		return -1;

	/* We want DOS semantics, ie allow non owner with write permission to change the
		bits on a file. Just like file_ntimes below.
	*/

	/* Check if we have write access. */
	if (CAN_WRITE(conn)) {
		/*
		 * We need to open the file with write access whilst
		 * still in our current user context. This ensures we
		 * are not violating security in doing the fchmod.
		 * This file open does *not* break any oplocks we are
		 * holding. We need to review this.... may need to
		 * break batch oplocks open by others. JRA.
		 */
		files_struct *fsp;
		if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
			return -1;
		become_root();
		ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
		unbecome_root();
		close_file_fchmod(fsp);
		notify_fname(conn, NOTIFY_ACTION_MODIFIED,
			     FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
		if (ret == 0) {
			st->st_mode = unixmode;
		}
	}

	return( ret );
}
Exemplo n.º 15
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
	struct smb_file_time ft;
	NTSTATUS status;
	struct share_mode_lock *lck = NULL;

	ZERO_STRUCT(ft);

	if (!fsp->update_write_time_on_close) {
		return NT_STATUS_OK;
	}

	if (null_timespec(fsp->close_write_time)) {
		fsp->close_write_time = timespec_current();
	}

	/* Ensure we have a valid stat struct for the source. */
	status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	if (!VALID_STAT(fsp->fsp_name->st)) {
		/* if it doesn't seem to be a real file */
		return NT_STATUS_OK;
	}

	/*
	 * get_existing_share_mode_lock() isn't really the right
	 * call here, as we're being called after
	 * close_remove_share_mode() inside close_normal_file()
	 * so it's quite normal to not have an existing share
	 * mode here. However, get_share_mode_lock() doesn't
	 * work because that will create a new share mode if
	 * one doesn't exist - so stick with this call (just
	 * ignore any error we get if the share mode doesn't
	 * exist.
	 */

	lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
	if (lck) {
		/* On close if we're changing the real file time we
		 * must update it in the open file db too. */
		(void)set_write_time(fsp->file_id, fsp->close_write_time);

		/* Close write times overwrite sticky write times
		   so we must replace any sticky write time here. */
		if (!null_timespec(lck->data->changed_write_time)) {
			(void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
		}
		TALLOC_FREE(lck);
	}

	ft.mtime = fsp->close_write_time;
	/* As this is a close based update, we are not directly changing the
	   file attributes from a client call, but indirectly from a write. */
	status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("update_write_time_on_close: smb_set_file_time "
			"on file %s returned %s\n",
			fsp_str_dbg(fsp),
			nt_errstr(status)));
		return status;
	}

	return status;
}
Exemplo n.º 16
0
static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
{
	fstring attrstr;
	files_struct *fsp = NULL;
	BOOL ret = False;

	if (!lp_store_dos_attributes(SNUM(conn))) {
		return False;
	}

	snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
	if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
		if((errno != EPERM) && (errno != EACCES)) {
			if (errno == ENOSYS
#if defined(ENOTSUP)
				|| errno == ENOTSUP) {
#else
				) {
#endif
				set_store_dos_attributes(SNUM(conn), False);
			}
			return False;
		}

		/* We want DOS semantics, ie allow non owner with write permission to change the
			bits on a file. Just like file_ntimes below.
		*/

		/* Check if we have write access. */
		if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
			return False;

		/*
		 * We need to open the file with write access whilst
		 * still in our current user context. This ensures we
		 * are not violating security in doing the setxattr.
		 */

		if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
			return ret;
		become_root();
		if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
			ret = True;
		}
		unbecome_root();
		close_file_fchmod(fsp);
		return ret;
	}
	DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
	return True;
}

/****************************************************************************
 Change a unix mode to a dos mode for an ms dfs link.
****************************************************************************/

uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
{
	uint32 result = 0;

	DEBUG(8,("dos_mode_msdfs: %s\n", path));

	if (!VALID_STAT(*sbuf)) {
		return 0;
	}

	/* First do any modifications that depend on the path name. */
	/* hide files with a name starting with a . */
	if (lp_hide_dot_files(SNUM(conn))) {
		const char *p = strrchr_m(path,'/');
		if (p) {
			p++;
		} else {
			p = path;
		}
		
		if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
			result |= aHIDDEN;
		}
	}
	
	result |= dos_mode_from_sbuf(conn, path, sbuf);

	/* Optimization : Only call is_hidden_path if it's not already
	   hidden. */
	if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
		result |= aHIDDEN;
	}

	DEBUG(8,("dos_mode_msdfs returning "));

	if (result & aHIDDEN) DEBUG(8, ("h"));
	if (result & aRONLY ) DEBUG(8, ("r"));
	if (result & aSYSTEM) DEBUG(8, ("s"));
	if (result & aDIR   ) DEBUG(8, ("d"));
	if (result & aARCH  ) DEBUG(8, ("a"));
	if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
	
	DEBUG(8,("\n"));

	return(result);
}
Exemplo n.º 17
0
bool can_delete_file_in_directory(connection_struct *conn,
				  const struct smb_filename *smb_fname)
{
	TALLOC_CTX *ctx = talloc_tos();
	char *dname = NULL;
	struct smb_filename *smb_fname_parent = NULL;
	NTSTATUS status;
	bool ret;

	if (!CAN_WRITE(conn)) {
		return False;
	}

	if (!lp_acl_check_permissions(SNUM(conn))) {
		/* This option means don't check. */
		return true;
	}

	/* Get the parent directory permission mask and owners. */
	if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) {
		return False;
	}

	status = create_synthetic_smb_fname(ctx, dname, NULL, NULL,
					    &smb_fname_parent);
	if (!NT_STATUS_IS_OK(status)) {
		ret = false;
		goto out;
	}

	if(SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
		ret = false;
		goto out;
	}

	/* fast paths first */

	if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) {
		ret = false;
		goto out;
	}
	if (get_current_uid(conn) == (uid_t)0) {
		/* I'm sorry sir, I didn't know you were root... */
		ret = true;
		goto out;
	}

#ifdef S_ISVTX
	/* sticky bit means delete only by owner of file or by root or
	 * by owner of directory. */
	if (smb_fname_parent->st.st_ex_mode & S_ISVTX) {
		if (!VALID_STAT(smb_fname->st)) {
			/* If the file doesn't already exist then
			 * yes we'll be able to delete it. */
			ret = true;
			goto out;
		}

		/*
		 * Patch from SATOH Fumiyasu <*****@*****.**>
		 * for bug #3348. Don't assume owning sticky bit
		 * directory means write access allowed.
		 * Fail to delete if we're not the owner of the file,
		 * or the owner of the directory as we have no possible
		 * chance of deleting. Otherwise, go on and check the ACL.
		 */
		if ((get_current_uid(conn) !=
			smb_fname_parent->st.st_ex_uid) &&
		    (get_current_uid(conn) != smb_fname->st.st_ex_uid)) {
			DEBUG(10,("can_delete_file_in_directory: not "
				  "owner of file %s or directory %s",
				  smb_fname_str_dbg(smb_fname),
				  smb_fname_str_dbg(smb_fname_parent)));
			ret = false;
			goto out;
		}
	}
#endif

	/* now for ACL checks */

	/*
	 * There's two ways to get the permission to delete a file: First by
	 * having the DELETE bit on the file itself and second if that does
	 * not help, by the DELETE_CHILD bit on the containing directory.
	 *
	 * Here we only check the directory permissions, we will
	 * check the file DELETE permission separately.
	 */

	ret = NT_STATUS_IS_OK(smbd_check_access_rights(conn,
				smb_fname_parent,
				FILE_DELETE_CHILD));
 out:
	TALLOC_FREE(dname);
	TALLOC_FREE(smb_fname_parent);
	return ret;
}
Exemplo n.º 18
0
static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
			 files_struct *fsp,
			 uint32_t security_info_sent,
			 const struct security_descriptor *psd)
{
	struct nfs4acl_config *config = NULL;
	const struct security_token *token = NULL;
	mode_t existing_mode;
	mode_t expected_mode;
	mode_t restored_mode;
	bool chown_needed = false;
	NTSTATUS status;
	int ret;

	SMB_VFS_HANDLE_GET_DATA(handle, config,
				struct nfs4acl_config,
				return NT_STATUS_INTERNAL_ERROR);

	if (!VALID_STAT(fsp->fsp_name->st)) {
		DBG_ERR("Invalid stat info on [%s]\n", fsp_str_dbg(fsp));
		return NT_STATUS_INTERNAL_ERROR;
	}

	existing_mode = fsp->fsp_name->st.st_ex_mode;
	if (S_ISDIR(existing_mode)) {
		expected_mode = 0777;
	} else {
		expected_mode = 0666;
	}
	if ((existing_mode & expected_mode) != expected_mode) {
		int saved_errno = 0;

		restored_mode = existing_mode | expected_mode;

		become_root();
		if (fsp->fh->fd != -1) {
			ret = SMB_VFS_NEXT_FCHMOD(handle,
						  fsp,
						  restored_mode);
		} else {
			ret = SMB_VFS_NEXT_CHMOD(handle,
						 fsp->fsp_name,
						 restored_mode);
		}
		if (ret != 0) {
			saved_errno = errno;
		}
		unbecome_root();
		if (saved_errno != 0) {
			errno = saved_errno;
		}
		if (ret != 0) {
			DBG_ERR("Resetting POSIX mode on [%s] from [0%o]: %s\n",
				fsp_str_dbg(fsp), existing_mode,
				strerror(errno));
			return map_nt_error_from_unix(errno);
		}
	}

	status = smb_set_nt_acl_nfs4(handle,
				     fsp,
				     &config->nfs4_params,
				     security_info_sent,
				     psd,
				     nfs4acl_smb4acl_set_fn);
	if (NT_STATUS_IS_OK(status)) {
		return NT_STATUS_OK;
	}

	/*
	 * We got access denied. If we're already root, or we didn't
	 * need to do a chown, or the fsp isn't open with WRITE_OWNER
	 * access, just return.
	 */

	if ((security_info_sent & SECINFO_OWNER) &&
	    (psd->owner_sid != NULL))
	{
		chown_needed = true;
	}
	if ((security_info_sent & SECINFO_GROUP) &&
	    (psd->group_sid != NULL))
	{
		chown_needed = true;
	}

	if (get_current_uid(handle->conn) == 0 ||
	    chown_needed == false ||
	    !(fsp->access_mask & SEC_STD_WRITE_OWNER))
	{
		return NT_STATUS_ACCESS_DENIED;
	}

	/*
	 * Only allow take-ownership, not give-ownership. That's the way Windows
	 * implements SEC_STD_WRITE_OWNER. MS-FSA 2.1.5.16 just states: If
	 * InputBuffer.OwnerSid is not a valid owner SID for a file in the
	 * objectstore, as determined in an implementation specific manner, the
	 * object store MUST return STATUS_INVALID_OWNER.
	 */
	token = get_current_nttok(fsp->conn);
	if (!security_token_is_sid(token, psd->owner_sid)) {
		return NT_STATUS_INVALID_OWNER;
	}

	DBG_DEBUG("overriding chown on file %s for sid %s\n",
		  fsp_str_dbg(fsp), sid_string_tos(psd->owner_sid));

	become_root();
	status = smb_set_nt_acl_nfs4(handle,
				     fsp,
				     &config->nfs4_params,
				     security_info_sent,
				     psd,
				     nfs4acl_smb4acl_set_fn);
	unbecome_root();
	return status;
}
Exemplo n.º 19
0
Arquivo: open.c Projeto: jophxy/samba
static BOOL open_file(files_struct *fsp,connection_struct *conn,
		      const char *fname1,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access)
{
	extern struct current_user current_user;
	pstring fname;
	int accmode = (flags & O_ACCMODE);
	int local_flags = flags;

	fsp->fd = -1;
	fsp->oplock_type = NO_OPLOCK;
	errno = EPERM;

	pstrcpy(fname,fname1);

	/* 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));
			check_for_pipe(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;
		}
	}

	/*
	 * 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." <*****@*****.**>
	 * 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;
	}

	/* actually do the open */

	if ((desired_access & (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 (VALID_STAT(*psbuf) && S_ISFIFO(psbuf->st_mode))
			local_flags |= O_NONBLOCK;
#endif
		fsp->fd = fd_open(conn, fname, local_flags, mode);

		if (fsp->fd == -1)  {
			DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n",
				 fname,strerror(errno),local_flags,flags));
			check_for_pipe(fname);
			return False;
		}
	} else
		fsp->fd = -1; /* What we used to call a stat open. */

	if (!VALID_STAT(*psbuf)) {
		int ret;

		if (fsp->fd == -1)
			ret = vfs_stat(conn, fname, psbuf);
		else {
			ret = vfs_fstat(fsp,fsp->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->size = psbuf->st_size;
	fsp->pos = -1;
	fsp->can_lock = True;
	fsp->can_read = ((flags & O_WRONLY)==0);
	fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
	fsp->share_mode = 0;
	fsp->desired_access = desired_access;
	fsp->print_file = False;
	fsp->modified = False;
	fsp->oplock_type = NO_OPLOCK;
	fsp->sent_oplock_break = NO_BREAK_SENT;
	fsp->is_directory = False;
	fsp->directory_delete_on_close = False;
	fsp->conn = conn;
	/*
	 * Note that the file name here is the *untranslated* name
	 * ie. it is still in the DOS codepage sent from the client.
	 * All use of this filename will pass though the sys_xxxx
	 * functions which will do the dos_to_unix translation before
	 * mapping into a UNIX filename. JRA.
	 */
	string_set(&fsp->fsp_name,fname);
	fsp->wbmpx_ptr = NULL;      
	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));

	return True;
}
Exemplo n.º 20
0
NTSTATUS unix_convert(connection_struct *conn,
			pstring name,
			BOOL allow_wcard_last_component,
			char *saved_last_component, 
			SMB_STRUCT_STAT *pst)
{
	SMB_STRUCT_STAT st;
	char *start, *end;
	pstring dirpath;
	pstring orig_path;
	BOOL component_was_mangled = False;
	BOOL name_has_wildcard = False;

	SET_STAT_INVALID(*pst);

	*dirpath = 0;

	if(saved_last_component) {
		*saved_last_component = 0;
	}

	if (conn->printer) {
		/* we don't ever use the filenames on a printer share as a
			filename - so don't convert them */
		return NT_STATUS_OK;
	}

	DEBUG(5, ("unix_convert called on file \"%s\"\n", name));

	/* 
	 * Conversion to basic unix format is already done in check_path_syntax().
	 */

	/* 
	 * Names must be relative to the root of the service - any leading /.
	 * and trailing /'s should have been trimmed by check_path_syntax().
	 */

#ifdef DEVELOPER
	SMB_ASSERT(*name != '/');
#endif

	/*
	 * If we trimmed down to a single '\0' character
	 * then we should use the "." directory to avoid
	 * searching the cache, but not if we are in a
	 * printing share.
	 * As we know this is valid we can return true here.
	 */

	if (!*name) {
		name[0] = '.';
		name[1] = '\0';
		if (SMB_VFS_STAT(conn,name,&st) == 0) {
			*pst = st;
		}
		DEBUG(5,("conversion finished \"\" -> %s\n",name));
		return NT_STATUS_OK;
	}

	if (name[0] == '.' && (name[1] == '/' || name[1] == '\0')) {
		/* Start of pathname can't be "." only. */
		if (name[1] == '\0' || name[2] == '\0') {
			return NT_STATUS_OBJECT_NAME_INVALID;
		} else {
			return determine_path_error(&name[2], allow_wcard_last_component);
		}
	}

	/*
	 * Ensure saved_last_component is valid even if file exists.
	 */

	if(saved_last_component) {
		end = strrchr_m(name, '/');
		if (end) {
			pstrcpy(saved_last_component, end + 1);
		} else {
			pstrcpy(saved_last_component, name);
		}
	}

	/*
	 * Large directory fix normalization. If we're case sensitive, and
	 * the case preserving parameters are set to "no", normalize the case of
	 * the incoming filename from the client WHETHER IT EXISTS OR NOT !
	 * This is in conflict with the current (3.0.20) man page, but is
	 * what people expect from the "large directory howto". I'll update
	 * the man page. Thanks to [email protected] for finding this. JRA.
	 */

	if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) {
		strnorm(name, lp_defaultcase(SNUM(conn)));
	}
	
	start = name;
	pstrcpy(orig_path, name);

	if(!conn->case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) {
		*pst = st;
		return NT_STATUS_OK;
	}

	/* 
	 * stat the name - if it exists then we are all done!
	 */

	if (SMB_VFS_STAT(conn,name,&st) == 0) {
		/* Ensure we catch all names with in "/."
		   this is disallowed under Windows. */
		const char *p = strstr(name, "/."); /* mb safe. */
		if (p) {
			if (p[2] == '/') {
				/* Error code within a pathname. */
				return NT_STATUS_OBJECT_PATH_NOT_FOUND;
			} else if (p[2] == '\0') {
				/* Error code at the end of a pathname. */
				return NT_STATUS_OBJECT_NAME_INVALID;
			}
		}
		/*
		 * This is a case insensitive file system, we really need to
		 * get the correct case of the name.
		 */
		if (!(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) {
		    pstring case_preserved_name;

		    if (SMB_VFS_GET_PRESERVED_NAME(conn, name, case_preserved_name)) {
			char * last_component = strrchr(name, '/');
			int space_left = PSTRING_LEN;

			if (last_component) {
				last_component++;
				*last_component = 0;
				space_left = PSTRING_LEN - strlen(name);
			} else
				last_component = name;
			strlcpy(last_component, case_preserved_name, space_left);
		    }
		}
		stat_cache_add(orig_path, name, conn->case_sensitive);
		DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
		*pst = st;
		return NT_STATUS_OK;
	}

	DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start));

	/* 
	 * A special case - if we don't have any mangling chars and are case
	 * sensitive then searching won't help.
	 */

	if (conn->case_sensitive && 
			!mangle_is_mangled(name, conn->params) &&
			!*lp_mangled_map(conn->params)) {
		return NT_STATUS_OK;
	}

	/* 
	 * is_mangled() was changed to look at an entire pathname, not 
	 * just a component. JRA.
	 */

	if (mangle_is_mangled(start, conn->params)) {
		component_was_mangled = True;
	}

	/* 
	 * Now we need to recursively match the name against the real 
	 * directory structure.
	 */

	/* 
	 * Match each part of the path name separately, trying the names
	 * as is first, then trying to scan the directory for matching names.
	 */

	for (; start ; start = (end?end+1:(char *)NULL)) {
		/* 
		 * Pinpoint the end of this section of the filename.
		 */
		end = strchr(start, '/'); /* mb safe. '/' can't be in any encoded char. */

		/* 
		 * Chop the name at this point.
		 */
		if (end) {
			*end = 0;
		}

		if (saved_last_component != 0) {
			pstrcpy(saved_last_component, end ? end + 1 : start);
		}

		/* The name cannot have a component of "." */

		if (ISDOT(start)) {
			if (!end)  {
				/* Error code at the end of a pathname. */
				return NT_STATUS_OBJECT_NAME_INVALID;
			}
			return determine_path_error(end+1, allow_wcard_last_component);
		}

		/* The name cannot have a wildcard if it's not
		   the last component. */

		name_has_wildcard = ms_has_wild(start);

		/* Wildcard not valid anywhere. */
		if (name_has_wildcard && !allow_wcard_last_component) {
			return NT_STATUS_OBJECT_NAME_INVALID;
		}

		/* Wildcards never valid within a pathname. */
		if (name_has_wildcard && end) {
			return NT_STATUS_OBJECT_NAME_INVALID;
		}

		/* 
		 * Check if the name exists up to this point.
		 */

		if (SMB_VFS_STAT(conn,name, &st) == 0) {
			/*
			 * It exists. it must either be a directory or this must be
			 * the last part of the path for it to be OK.
			 */
			if (end && !(st.st_mode & S_IFDIR)) {
				/*
				 * An intermediate part of the name isn't a directory.
				 */
				DEBUG(5,("Not a dir %s\n",start));
				*end = '/';
				/* 
				 * We need to return the fact that the intermediate
				 * name resolution failed. This is used to return an
				 * error of ERRbadpath rather than ERRbadfile. Some
				 * Windows applications depend on the difference between
				 * these two errors.
				 */
				return NT_STATUS_OBJECT_PATH_NOT_FOUND;
			}

			if (!end) {
				/*
				 * We just scanned for, and found the end of the path.
				 * We must return the valid stat struct.
				 * JRA.
				 */

				*pst = st;
			}

		} else {
			pstring rest;

			/* Stat failed - ensure we don't use it. */
			SET_STAT_INVALID(st);
			*rest = 0;

			/*
			 * Remember the rest of the pathname so it can be restored
			 * later.
			 */

			if (end) {
				pstrcpy(rest,end+1);
			}

			/* Reset errno so we can detect directory open errors. */
			errno = 0;

			/*
			 * Try to find this part of the path in the directory.
			 */

			if (name_has_wildcard || 
			    !scan_directory(conn, dirpath, start, sizeof(pstring) - 1 - (start - name))) {
				if (end) {
					/*
					 * An intermediate part of the name can't be found.
					 */
					DEBUG(5,("Intermediate not found %s\n",start));
					*end = '/';

					/* 
					 * We need to return the fact that the intermediate
					 * name resolution failed. This is used to return an
					 * error of ERRbadpath rather than ERRbadfile. Some
					 * Windows applications depend on the difference between
					 * these two errors.
					 */

					/* ENOENT, ENOTDIR and ELOOP all map to
					 * NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk. */

					if (errno == ENOENT ||
							errno == ENOTDIR ||
							errno == ELOOP) {
						return NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					return map_nt_error_from_unix(errno);
				}

				/* ENOENT is the only valid error here. */
				if (errno != ENOENT) {
					/* ENOTDIR and ELOOP both map to
					 * NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk. */
					if (errno == ENOTDIR ||
							errno == ELOOP) {
						return NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					return map_nt_error_from_unix(errno);
				}

				/*
				 * Just the last part of the name doesn't exist.
				 * We need to strupper() or strlower() it as
				 * this conversion may be used for file creation 
				 * purposes. Fix inspired by Thomas Neumann <*****@*****.**>.
				 */
				if (!conn->case_preserve ||
				    (mangle_is_8_3(start, False, conn->params) &&
						 !conn->short_case_preserve)) {
					strnorm(start, lp_defaultcase(SNUM(conn)));
				}

				/*
				 * check on the mangled stack to see if we can recover the 
				 * base of the filename.
				 */

				if (mangle_is_mangled(start, conn->params)) {
					mangle_check_cache( start, sizeof(pstring) - 1 - (start - name), conn->params);
				}

				DEBUG(5,("New file %s\n",start));
				return NT_STATUS_OK;
			}

			/* 
			 * Restore the rest of the string. If the string was mangled the size
			 * may have changed.
			 */
			if (end) {
				end = start + strlen(start);
				if (!safe_strcat(start, "/", sizeof(pstring) - 1 - (start - name)) ||
				    !safe_strcat(start, rest, sizeof(pstring) - 1 - (start - name))) {
					return map_nt_error_from_unix(ENAMETOOLONG);
				}
				*end = '\0';
			} else {
				/*
				 * We just scanned for, and found the end of the path.
				 * We must return a valid stat struct if it exists.
				 * JRA.
				 */

				if (SMB_VFS_STAT(conn,name, &st) == 0) {
					*pst = st;
				} else {
					SET_STAT_INVALID(st);
				}
			}
		} /* end else */

#ifdef DEVELOPER
		if (VALID_STAT(st) && get_delete_on_close_flag(st.st_dev, st.st_ino)) {
			return NT_STATUS_DELETE_PENDING;
		}
#endif

		/* 
		 * Add to the dirpath that we have resolved so far.
		 */
		if (*dirpath) {
			pstrcat(dirpath,"/");
		}

		pstrcat(dirpath,start);

		/*
		 * Don't cache a name with mangled or wildcard components
		 * as this can change the size.
		 */
		
		if(!component_was_mangled && !name_has_wildcard) {
			stat_cache_add(orig_path, dirpath, conn->case_sensitive);
		}
	
		/* 
		 * Restore the / that we wiped out earlier.
		 */
		if (end) {
			*end = '/';
		}
	}
  
	/*
	 * Don't cache a name with mangled or wildcard components
	 * as this can change the size.
	 */

	if(!component_was_mangled && !name_has_wildcard) {
		stat_cache_add(orig_path, name, conn->case_sensitive);
	}

	/* 
	 * The name has been resolved.
	 */

	DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
	return NT_STATUS_OK;
}
Exemplo n.º 21
0
/**
 * Go through all the steps to validate a filename.
 *
 * @param ctx		talloc_ctx to allocate memory with.
 * @param conn		connection struct for vfs calls.
 * @param dfs_path	Whether this path requires dfs resolution.
 * @param name_in	The unconverted name.
 * @param ucf_flags	flags to pass through to unix_convert().
 *			UCF_ALWAYS_ALLOW_WCARD_LCOMP will be OR'd in if
 *			p_cont_wcard != NULL and is true and
 *			UCF_COND_ALLOW_WCARD_LCOMP.
 * @param p_cont_wcard	If not NULL, will be set to true if the dfs path
 *			resolution detects a wildcard.
 * @param pp_smb_fname	The final converted name will be allocated if the
 *			return is NT_STATUS_OK.
 *
 * @return NT_STATUS_OK if all operations completed succesfully, appropriate
 * 	   error otherwise.
 */
NTSTATUS filename_convert(TALLOC_CTX *ctx,
				connection_struct *conn,
				bool dfs_path,
				const char *name_in,
				uint32_t ucf_flags,
				bool *ppath_contains_wcard,
				struct smb_filename **pp_smb_fname)
{
	NTSTATUS status;
	bool allow_wcards = (ucf_flags & (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP));
	char *fname = NULL;

	*pp_smb_fname = NULL;

	status = resolve_dfspath_wcard(ctx, conn,
				dfs_path,
				name_in,
				allow_wcards,
				&fname,
				ppath_contains_wcard);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("filename_convert: resolve_dfspath failed "
			"for name %s with %s\n",
			name_in,
			nt_errstr(status) ));
		return status;
	}

	if (is_fake_file_path(name_in)) {
		SMB_STRUCT_STAT st;
		ZERO_STRUCT(st);
		st.st_ex_nlink = 1;
		status = create_synthetic_smb_fname_split(ctx,
							  name_in,
							  &st,
							  pp_smb_fname);
		return status;
	}

	/*
	 * If the caller conditionally allows wildcard lookups, only add the
	 * always allow if the path actually does contain a wildcard.
	 */
	if (ucf_flags & UCF_COND_ALLOW_WCARD_LCOMP &&
	    ppath_contains_wcard != NULL && *ppath_contains_wcard) {
		ucf_flags |= UCF_ALWAYS_ALLOW_WCARD_LCOMP;
	}

	status = unix_convert(ctx, conn, fname, pp_smb_fname, ucf_flags);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("filename_convert: unix_convert failed "
			"for name %s with %s\n",
			fname,
			nt_errstr(status) ));
		return status;
	}

	if ((ucf_flags & UCF_UNIX_NAME_LOOKUP) &&
			VALID_STAT((*pp_smb_fname)->st) &&
			S_ISLNK((*pp_smb_fname)->st.st_ex_mode)) {
		return check_veto_path(conn, (*pp_smb_fname)->base_name);
	}

	status = check_name(conn, (*pp_smb_fname)->base_name);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(3,("filename_convert: check_name failed "
			"for name %s with %s\n",
			smb_fname_str_dbg(*pp_smb_fname),
			nt_errstr(status) ));
		TALLOC_FREE(*pp_smb_fname);
		return status;
	}

	return status;
}
Exemplo n.º 22
0
NTSTATUS unix_convert(TALLOC_CTX *ctx,
		      connection_struct *conn,
		      const char *orig_path,
		      struct smb_filename **smb_fname_out,
		      uint32_t ucf_flags)
{
	struct smb_filename *smb_fname = NULL;
	char *start, *end;
	char *dirpath = NULL;
	char *stream = NULL;
	bool component_was_mangled = False;
	bool name_has_wildcard = False;
	bool posix_pathnames = false;
	bool allow_wcard_last_component =
	    (ucf_flags & UCF_ALWAYS_ALLOW_WCARD_LCOMP);
	bool save_last_component = ucf_flags & UCF_SAVE_LCOMP;
	NTSTATUS status;
	int ret = -1;

	*smb_fname_out = NULL;

	smb_fname = talloc_zero(ctx, struct smb_filename);
	if (smb_fname == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	if (conn->printer) {
		/* we don't ever use the filenames on a printer share as a
			filename - so don't convert them */
		if (!(smb_fname->base_name = talloc_strdup(smb_fname,
							   orig_path))) {
			status = NT_STATUS_NO_MEMORY;
			goto err;
		}
		goto done;
	}

	DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path));

	/*
	 * Conversion to basic unix format is already done in
	 * check_path_syntax().
	 */

	/*
	 * Names must be relative to the root of the service - any leading /.
	 * and trailing /'s should have been trimmed by check_path_syntax().
	 */

#ifdef DEVELOPER
	SMB_ASSERT(*orig_path != '/');
#endif

	/*
	 * If we trimmed down to a single '\0' character
	 * then we should use the "." directory to avoid
	 * searching the cache, but not if we are in a
	 * printing share.
	 * As we know this is valid we can return true here.
	 */

	if (!*orig_path) {
		if (!(smb_fname->base_name = talloc_strdup(smb_fname, "."))) {
			status = NT_STATUS_NO_MEMORY;
			goto err;
		}
		if (SMB_VFS_STAT(conn, smb_fname) != 0) {
			status = map_nt_error_from_unix(errno);
			goto err;
		}
		DEBUG(5, ("conversion finished \"\" -> %s\n",
			  smb_fname->base_name));
		goto done;
	}

	if (orig_path[0] == '.' && (orig_path[1] == '/' ||
				orig_path[1] == '\0')) {
		/* Start of pathname can't be "." only. */
		if (orig_path[1] == '\0' || orig_path[2] == '\0') {
			status = NT_STATUS_OBJECT_NAME_INVALID;
		} else {
			status =determine_path_error(&orig_path[2],
			    allow_wcard_last_component);
		}
		goto err;
	}

	/* Start with the full orig_path as given by the caller. */
	if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto err;
	}

	/*
	 * Large directory fix normalization. If we're case sensitive, and
	 * the case preserving parameters are set to "no", normalize the case of
	 * the incoming filename from the client WHETHER IT EXISTS OR NOT !
	 * This is in conflict with the current (3.0.20) man page, but is
	 * what people expect from the "large directory howto". I'll update
	 * the man page. Thanks to [email protected] for finding this. JRA.
	 */

	if (conn->case_sensitive && !conn->case_preserve &&
			!conn->short_case_preserve) {
		strnorm(smb_fname->base_name, lp_defaultcase(SNUM(conn)));
	}

	/*
	 * Ensure saved_last_component is valid even if file exists.
	 */

	if(save_last_component) {
		end = strrchr_m(smb_fname->base_name, '/');
		if (end) {
			smb_fname->original_lcomp = talloc_strdup(smb_fname,
								  end + 1);
		} else {
			smb_fname->original_lcomp =
			    talloc_strdup(smb_fname, smb_fname->base_name);
		}
		if (smb_fname->original_lcomp == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto err;
		}
	}

	posix_pathnames = (lp_posix_pathnames() ||
				(ucf_flags & UCF_POSIX_PATHNAMES));

	/*
	 * Strip off the stream, and add it back when we're done with the
	 * base_name.
	 */
	if (!posix_pathnames) {
		stream = strchr_m(smb_fname->base_name, ':');

		if (stream != NULL) {
			char *tmp = talloc_strdup(smb_fname, stream);
			if (tmp == NULL) {
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
			/*
			 * Since this is actually pointing into
			 * smb_fname->base_name this truncates base_name.
			 */
			*stream = '\0';
			stream = tmp;
		}
	}

	start = smb_fname->base_name;

	/*
	 * If we're providing case insensitive semantics or
	 * the underlying filesystem is case insensitive,
	 * then a case-normalized hit in the stat-cache is
	 * authoratitive. JRA.
	 *
	 * Note: We're only checking base_name.  The stream_name will be
	 * added and verified in build_stream_path().
	 */

	if((!conn->case_sensitive || !(conn->fs_capabilities &
				       FILE_CASE_SENSITIVE_SEARCH)) &&
	    stat_cache_lookup(conn, posix_pathnames, &smb_fname->base_name, &dirpath, &start,
			      &smb_fname->st)) {
		goto done;
	}

	/*
	 * Make sure "dirpath" is an allocated string, we use this for
	 * building the directories with talloc_asprintf and free it.
	 */

	if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto err;
	}

	/*
	 * If we have a wildcard we must walk the path to
	 * find where the error is, even if case sensitive
	 * is true.
	 */

	name_has_wildcard = ms_has_wild(smb_fname->base_name);
	if (name_has_wildcard && !allow_wcard_last_component) {
		/* Wildcard not valid anywhere. */
		status = NT_STATUS_OBJECT_NAME_INVALID;
		goto fail;
	}

	DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n",
		 smb_fname->base_name, dirpath, start));

	if (!name_has_wildcard) {
		/*
		 * stat the name - if it exists then we can add the stream back (if
		 * there was one) and be done!
		 */

		if (posix_pathnames) {
			ret = SMB_VFS_LSTAT(conn, smb_fname);
		} else {
			ret = SMB_VFS_STAT(conn, smb_fname);
		}

		if (ret == 0) {
			status = check_for_dot_component(smb_fname);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}
			/* Add the path (not including the stream) to the cache. */
			stat_cache_add(orig_path, smb_fname->base_name,
				       conn->case_sensitive);
			DEBUG(5,("conversion of base_name finished %s -> %s\n",
				 orig_path, smb_fname->base_name));
			goto done;
		}

		/* Stat failed - ensure we don't use it. */
		SET_STAT_INVALID(smb_fname->st);

		if (errno == ENOENT) {
			/* Optimization when creating a new file - only
			   the last component doesn't exist. */
			status = check_parent_exists(ctx,
						conn,
						posix_pathnames,
						smb_fname,
						&dirpath,
						&start);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}
		}

		/*
		 * A special case - if we don't have any wildcards or mangling chars and are case
		 * sensitive or the underlying filesystem is case insensitive then searching
		 * won't help.
		 */

		if ((conn->case_sensitive || !(conn->fs_capabilities &
					FILE_CASE_SENSITIVE_SEARCH)) &&
				!mangle_is_mangled(smb_fname->base_name, conn->params)) {

			status = check_for_dot_component(smb_fname);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}

			/*
			 * The stat failed. Could be ok as it could be
 			 * a new file.
 			 */

			if (errno == ENOTDIR || errno == ELOOP) {
				status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			} else if (errno == ENOENT) {
				/*
				 * Was it a missing last component ?
				 * or a missing intermediate component ?
				 */
				struct smb_filename parent_fname;
				const char *last_component = NULL;

				ZERO_STRUCT(parent_fname);
				if (!parent_dirname(ctx, smb_fname->base_name,
							&parent_fname.base_name,
							&last_component)) {
					status = NT_STATUS_NO_MEMORY;
					goto fail;
				}
				if (posix_pathnames) {
					ret = SMB_VFS_LSTAT(conn, &parent_fname);
				} else {
					ret = SMB_VFS_STAT(conn, &parent_fname);
				}
				if (ret == -1) {
					if (errno == ENOTDIR ||
							errno == ENOENT ||
							errno == ELOOP) {
						status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
						goto fail;
					}
				}

				/*
				 * Missing last component is ok - new file.
				 * Also deal with permission denied elsewhere.
				 * Just drop out to done.
				 */
				goto done;
			}
		}
	} else {
		/*
		 * We have a wildcard in the pathname.
		 *
		 * Optimization for common case where the wildcard
		 * is in the last component and the client already
		 * sent the correct case.
		 */
		status = check_parent_exists(ctx,
					conn,
					posix_pathnames,
					smb_fname,
					&dirpath,
					&start);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
	}

	/*
	 * is_mangled() was changed to look at an entire pathname, not
	 * just a component. JRA.
	 */

	if (mangle_is_mangled(start, conn->params)) {
		component_was_mangled = True;
	}

	/*
	 * Now we need to recursively match the name against the real
	 * directory structure.
	 */

	/*
	 * Match each part of the path name separately, trying the names
	 * as is first, then trying to scan the directory for matching names.
	 */

	for (; start ; start = (end?end+1:(char *)NULL)) {
		/*
		 * Pinpoint the end of this section of the filename.
		 */
		/* mb safe. '/' can't be in any encoded char. */
		end = strchr(start, '/');

		/*
		 * Chop the name at this point.
		 */
		if (end) {
			*end = 0;
		}

		if (save_last_component) {
			TALLOC_FREE(smb_fname->original_lcomp);
			smb_fname->original_lcomp = talloc_strdup(smb_fname,
							end ? end + 1 : start);
			if (!smb_fname->original_lcomp) {
				DEBUG(0, ("talloc failed\n"));
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
		}

		/* The name cannot have a component of "." */

		if (ISDOT(start)) {
			if (!end)  {
				/* Error code at the end of a pathname. */
				status = NT_STATUS_OBJECT_NAME_INVALID;
			} else {
				status = determine_path_error(end+1,
						allow_wcard_last_component);
			}
			goto fail;
		}

		/* The name cannot have a wildcard if it's not
		   the last component. */

		name_has_wildcard = ms_has_wild(start);

		/* Wildcards never valid within a pathname. */
		if (name_has_wildcard && end) {
			status = NT_STATUS_OBJECT_NAME_INVALID;
			goto fail;
		}

		/* Skip the stat call if it's a wildcard end. */
		if (name_has_wildcard) {
			DEBUG(5,("Wildcard %s\n",start));
			goto done;
		}

		/*
		 * Check if the name exists up to this point.
		 */

		if (posix_pathnames) {
			ret = SMB_VFS_LSTAT(conn, smb_fname);
		} else {
			ret = SMB_VFS_STAT(conn, smb_fname);
		}

		if (ret == 0) {
			/*
			 * It exists. it must either be a directory or this must
			 * be the last part of the path for it to be OK.
			 */
			if (end && !S_ISDIR(smb_fname->st.st_ex_mode)) {
				/*
				 * An intermediate part of the name isn't
				 * a directory.
				 */
				DEBUG(5,("Not a dir %s\n",start));
				*end = '/';
				/*
				 * We need to return the fact that the
				 * intermediate name resolution failed. This
				 * is used to return an error of ERRbadpath
				 * rather than ERRbadfile. Some Windows
				 * applications depend on the difference between
				 * these two errors.
				 */
				status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			}

		} else {
			char *found_name = NULL;

			/* Stat failed - ensure we don't use it. */
			SET_STAT_INVALID(smb_fname->st);

			/*
			 * Reset errno so we can detect
			 * directory open errors.
			 */
			errno = 0;

			/*
			 * Try to find this part of the path in the directory.
			 */

			if (name_has_wildcard ||
			    (get_real_filename(conn, dirpath, start,
					       talloc_tos(),
					       &found_name) == -1)) {
				char *unmangled;

				if (end) {
					/*
					 * An intermediate part of the name
					 * can't be found.
					 */
					DEBUG(5,("Intermediate not found %s\n",
							start));
					*end = '/';

					/*
					 * We need to return the fact that the
					 * intermediate name resolution failed.
					 * This is used to return an error of
					 * ERRbadpath rather than ERRbadfile.
					 * Some Windows applications depend on
					 * the difference between these two
					 * errors.
					 */

					/*
					 * ENOENT, ENOTDIR and ELOOP all map
					 * to NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */

					if (errno == ENOENT ||
							errno == ENOTDIR ||
							errno == ELOOP) {
						status =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					else {
						status =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/*
				 * ENOENT/EACCESS are the only valid errors
				 * here. EACCESS needs handling here for
				 * "dropboxes", i.e. directories where users
				 * can only put stuff with permission -wx.
				 */
				if ((errno != 0) && (errno != ENOENT)
				    && (errno != EACCES)) {
					/*
					 * ENOTDIR and ELOOP both map to
					 * NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */
					if (errno == ENOTDIR ||
							errno == ELOOP) {
						status =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					} else {
						status =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/*
				 * Just the last part of the name doesn't exist.
				 * We need to strupper() or strlower() it as
				 * this conversion may be used for file creation
				 * purposes. Fix inspired by
				 * Thomas Neumann <*****@*****.**>.
				 */
				if (!conn->case_preserve ||
				    (mangle_is_8_3(start, False,
						   conn->params) &&
						 !conn->short_case_preserve)) {
					strnorm(start,
						lp_defaultcase(SNUM(conn)));
				}

				/*
				 * check on the mangled stack to see if we can
				 * recover the base of the filename.
				 */

				if (mangle_is_mangled(start, conn->params)
				    && mangle_lookup_name_from_8_3(ctx,
					    		start,
							&unmangled,
							conn->params)) {
					char *tmp;
					size_t start_ofs =
					    start - smb_fname->base_name;

					if (*dirpath != '\0') {
						tmp = talloc_asprintf(
							smb_fname, "%s/%s",
							dirpath, unmangled);
						TALLOC_FREE(unmangled);
					}
					else {
						tmp = unmangled;
					}
					if (tmp == NULL) {
						DEBUG(0, ("talloc failed\n"));
						status = NT_STATUS_NO_MEMORY;
						goto err;
					}
					TALLOC_FREE(smb_fname->base_name);
					smb_fname->base_name = tmp;
					start =
					    smb_fname->base_name + start_ofs;
					end = start + strlen(start);
				}

				DEBUG(5,("New file %s\n",start));
				goto done;
			}


			/*
			 * Restore the rest of the string. If the string was
			 * mangled the size may have changed.
			 */
			if (end) {
				char *tmp;
				size_t start_ofs =
				    start - smb_fname->base_name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(smb_fname,
						"%s/%s/%s", dirpath,
						found_name, end+1);
				}
				else {
					tmp = talloc_asprintf(smb_fname,
						"%s/%s", found_name,
						end+1);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc_asprintf failed\n"));
					status = NT_STATUS_NO_MEMORY;
					goto err;
				}
				TALLOC_FREE(smb_fname->base_name);
				smb_fname->base_name = tmp;
				start = smb_fname->base_name + start_ofs;
				end = start + strlen(found_name);
				*end = '\0';
			} else {
				char *tmp;
				size_t start_ofs =
				    start - smb_fname->base_name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(smb_fname,
						"%s/%s", dirpath,
						found_name);
				} else {
					tmp = talloc_strdup(smb_fname,
						found_name);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc failed\n"));
					status = NT_STATUS_NO_MEMORY;
					goto err;
				}
				TALLOC_FREE(smb_fname->base_name);
				smb_fname->base_name = tmp;
				start = smb_fname->base_name + start_ofs;

				/*
				 * We just scanned for, and found the end of
				 * the path. We must return a valid stat struct
				 * if it exists. JRA.
				 */

				if (posix_pathnames) {
					ret = SMB_VFS_LSTAT(conn, smb_fname);
				} else {
					ret = SMB_VFS_STAT(conn, smb_fname);
				}

				if (ret != 0) {
					SET_STAT_INVALID(smb_fname->st);
				}
			}

			TALLOC_FREE(found_name);
		} /* end else */

#ifdef DEVELOPER
		/*
		 * This sucks!
		 * We should never provide different behaviors
		 * depending on DEVELOPER!!!
		 */
		if (VALID_STAT(smb_fname->st)) {
			bool delete_pending;
			uint32_t name_hash;

			status = file_name_hash(conn,
					smb_fname_str_dbg(smb_fname),
					&name_hash);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}

			get_file_infos(vfs_file_id_from_sbuf(conn,
							     &smb_fname->st),
				       name_hash,
				       &delete_pending, NULL);
			if (delete_pending) {
				status = NT_STATUS_DELETE_PENDING;
				goto fail;
			}
		}
#endif

		/*
		 * Add to the dirpath that we have resolved so far.
		 */

		if (*dirpath != '\0') {
			char *tmp = talloc_asprintf(ctx,
					"%s/%s", dirpath, start);
			if (!tmp) {
				DEBUG(0, ("talloc_asprintf failed\n"));
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
			TALLOC_FREE(dirpath);
			dirpath = tmp;
		}
		else {
			TALLOC_FREE(dirpath);
			if (!(dirpath = talloc_strdup(ctx,start))) {
				DEBUG(0, ("talloc_strdup failed\n"));
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
		}

		/*
		 * Cache the dirpath thus far. Don't cache a name with mangled
		 * or wildcard components as this can change the size.
		 */
		if(!component_was_mangled && !name_has_wildcard) {
			stat_cache_add(orig_path, dirpath,
					conn->case_sensitive);
		}

		/*
		 * Restore the / that we wiped out earlier.
		 */
		if (end) {
			*end = '/';
		}
	}

	/*
	 * Cache the full path. Don't cache a name with mangled or wildcard
	 * components as this can change the size.
	 */

	if(!component_was_mangled && !name_has_wildcard) {
		stat_cache_add(orig_path, smb_fname->base_name,
			       conn->case_sensitive);
	}

	/*
	 * The name has been resolved.
	 */

	DEBUG(5,("conversion finished %s -> %s\n", orig_path,
		 smb_fname->base_name));

 done:
	/* Add back the stream if one was stripped off originally. */
	if (stream != NULL) {
		smb_fname->stream_name = stream;

		/* Check path now that the base_name has been converted. */
		status = build_stream_path(ctx, conn, orig_path, smb_fname);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
	}
	TALLOC_FREE(dirpath);
	*smb_fname_out = smb_fname;
	return NT_STATUS_OK;
 fail:
	DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start));
	if (*dirpath != '\0') {
		smb_fname->base_name = talloc_asprintf(smb_fname, "%s/%s",
						       dirpath, start);
	} else {
		smb_fname->base_name = talloc_strdup(smb_fname, start);
	}
	if (!smb_fname->base_name) {
		DEBUG(0, ("talloc_asprintf failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto err;
	}

	*smb_fname_out = smb_fname;
	TALLOC_FREE(dirpath);
	return status;
 err:
	TALLOC_FREE(smb_fname);
	return status;
}
Exemplo n.º 23
0
static bool get_ea_dos_attribute(connection_struct *conn,
				 struct smb_filename *smb_fname,
				 uint32 *pattr)
{
	struct xattr_DOSATTRIB dosattrib;
	enum ndr_err_code ndr_err;
	DATA_BLOB blob;
	ssize_t sizeret;
	fstring attrstr;
	uint32_t dosattr;

	if (!lp_store_dos_attributes(SNUM(conn))) {
		return False;
	}

	/* Don't reset pattr to zero as we may already have filename-based attributes we
	   need to preserve. */

	sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
				   SAMBA_XATTR_DOS_ATTRIB, attrstr,
				   sizeof(attrstr));
	if (sizeret == -1) {
		if (errno == ENOSYS
#if defined(ENOTSUP)
			|| errno == ENOTSUP) {
#else
				) {
#endif
			DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
				 "from EA on file %s: Error = %s\n",
				 smb_fname_str_dbg(smb_fname),
				 strerror(errno)));
			set_store_dos_attributes(SNUM(conn), False);
		}
		return False;
	}

	blob.data = (uint8_t *)attrstr;
	blob.length = sizeret;

	ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
			(ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);

	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
			 "from EA on file %s: Error = %s\n",
			 smb_fname_str_dbg(smb_fname),
			 ndr_errstr(ndr_err)));
		return false;
	}

	DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
		  smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));

	switch (dosattrib.version) {
		case 0xFFFF:
			dosattr = dosattrib.info.compatinfoFFFF.attrib;
			break;
		case 1:
			dosattr = dosattrib.info.info1.attrib;
			if (!null_nttime(dosattrib.info.info1.create_time)) {
				struct timespec create_time =
					nt_time_to_unix_timespec(
						dosattrib.info.info1.create_time);

				update_stat_ex_create_time(&smb_fname->st,
							create_time);

				DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
					"set btime %s\n",
					smb_fname_str_dbg(smb_fname),
					time_to_asc(convert_timespec_to_time_t(
						create_time)) ));
			}
			break;
		case 2:
			dosattr = dosattrib.info.oldinfo2.attrib;
			/* Don't know what flags to check for this case. */
			break;
		case 3:
			dosattr = dosattrib.info.info3.attrib;
			if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
					!null_nttime(dosattrib.info.info3.create_time)) {
				struct timespec create_time =
					nt_time_to_unix_timespec(
						dosattrib.info.info3.create_time);

				update_stat_ex_create_time(&smb_fname->st,
							create_time);

				DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
					"set btime %s\n",
					smb_fname_str_dbg(smb_fname),
					time_to_asc(convert_timespec_to_time_t(
						create_time)) ));
			}
			break;
		default:
			DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
				 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
				 attrstr));
	                return false;
	}

	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
		dosattr |= FILE_ATTRIBUTE_DIRECTORY;
	}
	/* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
	*pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));

	DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));

	if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
	if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
	if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
	if (dosattr & FILE_ATTRIBUTE_DIRECTORY   ) DEBUG(8, ("d"));
	if (dosattr & FILE_ATTRIBUTE_ARCHIVE  ) DEBUG(8, ("a"));

	DEBUG(8,("\n"));

	return True;
}

/****************************************************************************
 Set DOS attributes in an EA.
 Also sets the create time.
****************************************************************************/

static bool set_ea_dos_attribute(connection_struct *conn,
				 struct smb_filename *smb_fname,
				 uint32 dosmode)
{
	struct xattr_DOSATTRIB dosattrib;
	enum ndr_err_code ndr_err;
	DATA_BLOB blob;

	ZERO_STRUCT(dosattrib);
	ZERO_STRUCT(blob);

	dosattrib.version = 3;
	dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
					XATTR_DOSINFO_CREATE_TIME;
	dosattrib.info.info3.attrib = dosmode;
	dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
				smb_fname->st.st_ex_btime);

	DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
		(unsigned int)dosmode,
		time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
		smb_fname_str_dbg(smb_fname) ));

	ndr_err = ndr_push_struct_blob(
			&blob, talloc_tos(), &dosattrib,
			(ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);

	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
			ndr_errstr(ndr_err)));
		return false;
	}

	if (blob.data == NULL || blob.length == 0) {
		return false;
	}

	if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
			     SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
			     0) == -1) {
		bool ret = false;
		bool need_close = false;
		files_struct *fsp = NULL;

		if((errno != EPERM) && (errno != EACCES)) {
			if (errno == ENOSYS
#if defined(ENOTSUP)
				|| errno == ENOTSUP) {
#else
				) {
#endif
				DEBUG(1,("set_ea_dos_attributes: Cannot set "
					 "attribute EA on file %s: Error = %s\n",
					 smb_fname_str_dbg(smb_fname),
					 strerror(errno) ));
				set_store_dos_attributes(SNUM(conn), False);
			}
			return false;
		}

		/* We want DOS semantics, ie allow non owner with write permission to change the
			bits on a file. Just like file_ntimes below.
		*/

		/* Check if we have write access. */
		if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
			return false;

		if (!can_write_to_file(conn, smb_fname)) {
			return false;
		}

		/*
		 * We need to get an open file handle to do the
		 * metadata operation under root.
		 */

		if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn,
						smb_fname,
						&fsp,
						&need_close))) {
			return false;
		}

		become_root();
		if (SMB_VFS_FSETXATTR(fsp,
				     SAMBA_XATTR_DOS_ATTRIB, blob.data,
				     blob.length, 0) == 0) {
			ret = true;
		}
		unbecome_root();
		if (need_close) {
			close_file(NULL, fsp, NORMAL_CLOSE);
		}
		return ret;
	}
	DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
		(unsigned int)dosmode,
		smb_fname_str_dbg(smb_fname)));
	return true;
}

/****************************************************************************
 Change a unix mode to a dos mode for an ms dfs link.
****************************************************************************/

uint32 dos_mode_msdfs(connection_struct *conn,
		      const struct smb_filename *smb_fname)
{
	uint32 result = 0;

	DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));

	if (!VALID_STAT(smb_fname->st)) {
		return 0;
	}

	/* First do any modifications that depend on the path name. */
	/* hide files with a name starting with a . */
	if (lp_hide_dot_files(SNUM(conn))) {
		const char *p = strrchr_m(smb_fname->base_name, '/');
		if (p) {
			p++;
		} else {
			p = smb_fname->base_name;
		}

		/* Only . and .. are not hidden. */
		if (p[0] == '.' && !((p[1] == '\0') ||
				(p[1] == '.' && p[2] == '\0'))) {
			result |= FILE_ATTRIBUTE_HIDDEN;
		}
	}

	result |= dos_mode_from_sbuf(conn, smb_fname);

	/* Optimization : Only call is_hidden_path if it's not already
	   hidden. */
	if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
	    IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
		result |= FILE_ATTRIBUTE_HIDDEN;
	}

	if (result == 0) {
		result = FILE_ATTRIBUTE_NORMAL;
	}

	result = filter_mode_by_protocol(result);

	/*
	 * Add in that it is a reparse point
	 */
	result |= FILE_ATTRIBUTE_REPARSE_POINT;

	DEBUG(8,("dos_mode_msdfs returning "));

	if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
	if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
	if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
	if (result & FILE_ATTRIBUTE_DIRECTORY   ) DEBUG(8, ("d"));
	if (result & FILE_ATTRIBUTE_ARCHIVE  ) DEBUG(8, ("a"));
	if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));

	DEBUG(8,("\n"));

	return(result);
}
Exemplo n.º 24
0
uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
{
	uint32 result = 0;
	bool offline;

	DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));

	if (!VALID_STAT(smb_fname->st)) {
		return 0;
	}

	/* First do any modifications that depend on the path name. */
	/* hide files with a name starting with a . */
	if (lp_hide_dot_files(SNUM(conn))) {
		const char *p = strrchr_m(smb_fname->base_name,'/');
		if (p) {
			p++;
		} else {
			p = smb_fname->base_name;
		}

		/* Only . and .. are not hidden. */
		if (p[0] == '.' && !((p[1] == '\0') ||
				(p[1] == '.' && p[2] == '\0'))) {
			result |= FILE_ATTRIBUTE_HIDDEN;
		}
	}

	/* Get the DOS attributes from an EA by preference. */
	if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
		result |= dos_mode_from_sbuf(conn, smb_fname);
	}

	offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
	if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
		result |= FILE_ATTRIBUTE_OFFLINE;
	}

	if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
		bool compressed = false;
		NTSTATUS status = dos_mode_check_compressed(conn, smb_fname,
							    &compressed);
		if (NT_STATUS_IS_OK(status) && compressed) {
			result |= FILE_ATTRIBUTE_COMPRESSED;
		}
	}

	/* Optimization : Only call is_hidden_path if it's not already
	   hidden. */
	if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
	    IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
		result |= FILE_ATTRIBUTE_HIDDEN;
	}

	if (result == 0) {
		result = FILE_ATTRIBUTE_NORMAL;
	}

	result = filter_mode_by_protocol(result);

	dos_mode_debug_print(result);

	return result;
}
Exemplo n.º 25
0
Arquivo: durable.c Projeto: hef/samba
NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
					const DATA_BLOB old_cookie,
					TALLOC_CTX *mem_ctx,
					DATA_BLOB *new_cookie)
{
	struct connection_struct *conn = fsp->conn;
	NTSTATUS status;
	enum ndr_err_code ndr_err;
	struct vfs_default_durable_cookie cookie;
	DATA_BLOB new_cookie_blob = data_blob_null;
	struct share_mode_lock *lck;
	bool ok;

	*new_cookie = data_blob_null;

	ZERO_STRUCT(cookie);

	ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
			(ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		status = ndr_map_error2ntstatus(ndr_err);
		return status;
	}

	if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (!file_id_equal(&fsp->file_id, &cookie.id)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	/*
	 * For now let it be simple and do not keep
	 * delete on close files durable open
	 */
	if (fsp->initial_delete_on_close) {
		return NT_STATUS_NOT_SUPPORTED;
	}
	if (fsp->delete_on_close) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	if (!VALID_STAT(fsp->fsp_name->st)) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	/* Ensure any pending write time updates are done. */
	if (fsp->update_write_time_event) {
		update_write_time_handler(fsp->conn->sconn->ev_ctx,
					fsp->update_write_time_event,
					timeval_current(),
					(void *)fsp);
	}

	/*
	 * The above checks are done in mark_share_mode_disconnected() too
	 * but we want to avoid getting the lock if possible
	 */
	lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
	if (lck != NULL) {
		struct smb_file_time ft;

		ZERO_STRUCT(ft);

		if (fsp->write_time_forced) {
			ft.mtime = lck->data->changed_write_time;
		} else if (fsp->update_write_time_on_close) {
			if (null_timespec(fsp->close_write_time)) {
				ft.mtime = timespec_current();
			} else {
				ft.mtime = fsp->close_write_time;
			}
		}

		if (!null_timespec(ft.mtime)) {
			round_timespec(conn->ts_res, &ft.mtime);
			file_ntimes(conn, fsp->fsp_name, &ft);
		}

		ok = mark_share_mode_disconnected(lck, fsp);
		if (!ok) {
			TALLOC_FREE(lck);
		}
	}
	if (lck != NULL) {
		ok = brl_mark_disconnected(fsp);
		if (!ok) {
			TALLOC_FREE(lck);
		}
	}
	if (lck == NULL) {
		return NT_STATUS_NOT_SUPPORTED;
	}
	TALLOC_FREE(lck);

	status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	ZERO_STRUCT(cookie);
	cookie.allow_reconnect = true;
	cookie.id = fsp->file_id;
	cookie.servicepath = conn->connectpath;
	cookie.base_name = fsp->fsp_name->base_name;
	cookie.initial_allocation_size = fsp->initial_allocation_size;
	cookie.position_information = fsp->fh->position_information;
	cookie.update_write_time_triggered = fsp->update_write_time_triggered;
	cookie.update_write_time_on_close = fsp->update_write_time_on_close;
	cookie.write_time_forced = fsp->write_time_forced;
	cookie.close_write_time = fsp->close_write_time;

	cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
	cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
	cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
	cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
	cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
	cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
	cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
	cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
	cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
	cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
	cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
	cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
	cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
	cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
	cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
	cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
	cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;

	ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
			(ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		status = ndr_map_error2ntstatus(ndr_err);
		return status;
	}

	status = fd_close(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		data_blob_free(&new_cookie_blob);
		return status;
	}

	*new_cookie = new_cookie_blob;
	return NT_STATUS_OK;
}