Exemple #1
0
int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
		     uint32 dosmode, const char *parent_dir, bool newfile)
{
	int mask=0;
	mode_t tmp;
	mode_t unixmode;
	int ret = -1, lret = -1;
	uint32_t old_mode;
	struct timespec new_create_timespec;
	files_struct *fsp = NULL;
	bool need_close = false;
	NTSTATUS status;

	if (!CAN_WRITE(conn)) {
		errno = EROFS;
		return -1;
	}

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

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

	unixmode = smb_fname->st.st_ex_mode;

	get_acl_group_bits(conn, smb_fname->base_name,
			   &smb_fname->st.st_ex_mode);

	if (S_ISDIR(smb_fname->st.st_ex_mode))
		dosmode |= FILE_ATTRIBUTE_DIRECTORY;
	else
		dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;

	new_create_timespec = smb_fname->st.st_ex_btime;

	old_mode = dos_mode(conn, smb_fname);

	if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
	    !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
		lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
		if (lret == -1) {
			if (errno == ENOTSUP) {
				DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
					   "%s/%s is not supported.\n",
					   parent_dir,
					   smb_fname_str_dbg(smb_fname)));
			} else {
				DEBUG(0, ("An error occurred while setting "
					  "FILE_ATTRIBUTE_OFFLINE for "
					  "%s/%s: %s", parent_dir,
					  smb_fname_str_dbg(smb_fname),
					  strerror(errno)));
			}
		}
	}

	dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
	old_mode &= ~FILE_ATTRIBUTE_OFFLINE;

	smb_fname->st.st_ex_btime = new_create_timespec;

	/* Store the DOS attributes in an EA by preference. */
	if (lp_store_dos_attributes(SNUM(conn))) {
		/*
		 * Don't fall back to using UNIX modes. Finally
		 * follow the smb.conf manpage.
		 */
		if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
			return -1;
		}
		if (!newfile) {
			notify_fname(conn, NOTIFY_ACTION_MODIFIED,
				     FILE_NOTIFY_CHANGE_ATTRIBUTES,
				     smb_fname->base_name);
		}
		smb_fname->st.st_ex_mode = unixmode;
		return 0;
	}

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

	/* preserve the file type bits */
	mask |= S_IFMT;

	/* 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 |= (smb_fname->st.st_ex_mode & mask);

	/* if we previously had any r bits set then leave them alone */
	if ((tmp = smb_fname->st.st_ex_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 |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
	}

	/*
	 * From the chmod 2 man page:
	 *
	 * "If the calling process is not privileged, and the group of the file
	 * does not match the effective group ID of the process or one of its
	 * supplementary group IDs, the S_ISGID bit will be turned off, but
	 * this will not cause an error to be returned."
	 *
	 * Simply refuse to do the chmod in this case.
	 */

	if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
			geteuid() != sec_initial_uid() &&
			!current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
		DEBUG(3,("file_set_dosmode: setgid bit cannot be "
			"set for directory %s\n",
			smb_fname_str_dbg(smb_fname)));
		errno = EPERM;
		return -1;
	}

	ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
	if (ret == 0) {
		if(!newfile || (lret != -1)) {
			notify_fname(conn, NOTIFY_ACTION_MODIFIED,
				     FILE_NOTIFY_CHANGE_ATTRIBUTES,
				     smb_fname->base_name);
		}
		smb_fname->st.st_ex_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.
	*/

	if (!can_write_to_file(conn, smb_fname)) {
		errno = EACCES;
		return -1;
	}

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

	status = get_file_handle_for_metadata(conn,
					      smb_fname,
					      &fsp,
					      &need_close);
	if (!NT_STATUS_IS_OK(status)) {
		errno = map_errno_from_nt_status(status);
		return -1;
	}

	become_root();
	ret = SMB_VFS_FCHMOD(fsp, unixmode);
	unbecome_root();
	if (need_close) {
		close_file(NULL, fsp, NORMAL_CLOSE);
	}
	if (!newfile) {
		notify_fname(conn, NOTIFY_ACTION_MODIFIED,
			     FILE_NOTIFY_CHANGE_ATTRIBUTES,
			     smb_fname->base_name);
	}
	if (ret == 0) {
		smb_fname->st.st_ex_mode = unixmode;
	}

	return( ret );
}
Exemple #2
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);
}
Exemple #3
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);
}
Exemple #4
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 );
}
Exemple #5
0
static bool set_ea_dos_attribute(connection_struct *conn,
				 struct smb_filename *smb_fname,
				 uint32_t 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)) {
			DBG_INFO("Cannot set "
				 "attribute EA on file %s: Error = %s\n",
				 smb_fname_str_dbg(smb_fname), strerror(errno));
			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;
}