[PATCH] smbd: Added support for IO_REPARSE_TAG_MOUNTPOINT

Jeremy Allison jra at samba.org
Sat Sep 30 01:14:12 UTC 2017


On Tue, Sep 19, 2017 at 11:21:19AM -0600, Jacob Holtom via samba-technical wrote:
> Hello all,
> 
> Attached is an updated patch for IO_REPARSE_TAG_MOUNTPOINT.
> 
> Please review,
> 
> Thanks!
> 
> Jacob Holtom

Hi Jacob, I really don't think this is the right way
to do this.

Give me a little time and I'll explain fully why later
next week, sorry.

Regards,

	Jeremy.


> From 4c4595c8e8ab935d21a0eb5b99638ce1d5a64ca4 Mon Sep 17 00:00:00 2001
> From: Jacob Holtom <jacob at et.byu.edu>
> Date: Mon, 18 Sep 2017 16:25:30 -0600
> Subject: [PATCH] smbd: Added support for IO_REPARSE_TAG_MOUNTPOINT
> 
> This patch adds support for returning IO_REPARSE_TAG_MOUNTPOINT when a device
> boundary is crossed.  Doing so resolves issues with Windows Explorer's pre-copy
> freespace check incorrectly denying or allowing a copy operation based on the
> base_share_dev dfree/quota data, instead of the target device's dfree/quota
> data, where the target device is a separate submounted or widelinked device.
> Device boundary crossings are detected as a requested path is resolved, using a
> combination of the connection_struct's base_share_dev member, and a newly added
> curr_share_dev member.
> 
> If the requested path's device is not equal to the base_share_dev or the
> curr_share_dev, the IO_REPARSE_TAG_MOUNTPOINT is added to the response, and the
> curr_share_dev is update to the resolved device's identifier.
> 
> Using both base_share_dev and curr_share_dev members is required to correctly
> update dfree/quota data when descending and ascending a path tree, but also
> allows for nesting submounts (or widelinked devices) to any depth.
> 
> In which case, curr_share_dev is updated with the target path's device, and
> IO_REPARSE_TAG_MOUNTPOINT is returned to the client.
> 
> This added behavior improves how the client handles quota and dfree requests for
> different submounted/widelinked devices.  The client is now aware when it
> crosses a device boundary and must re-request dfree/quota data.
> 
> An administrator can now use this behavior to expand available space in an
> existing tree heirarchy using submounts (similar to file share's containing NTFS
> Junctions) without requiring DFS links to a separate share.
> 
> An admin could also now create a basic equivalent of folder quota by widelinking
> (or bind mounting) to a directory on a separate device with a user and/or group
> quota specific to directory on the target device (e.g. user's "home" has a
> "subfolder" widelink to /mnt/quota_volume/subfolder, where the target
> /mnt/quota_volume/subfolder has setgid bit set and a quota applied to the
> primary group.
> 
> It can also be seen that the correct directory paths are passed to the "get
> quota command" script, allowing the script to be queried as intended without the
> caveat stated in the man page that 'The directory is actually mostly just "." -
> It needs to be ...' Since quota's are maintained by device on Linux (with the
> exception of a few filesystems), this patch improves the behavior for a majority
> of quota using users.
> 
> A possibly confusing behavior with the Windows Explorer UX is present when a
> widelink to an AutoFS mountpoint is not using the browse option.  The widelink
> target is not resolved by the server until the widelink is traversed, and so the
> widelink will appear with a normal folder icon.  If the client descends into the
> widelink, then moves up to the parent, the widelink changes to a folder with a
> blue link arrow in the corner of the icon.  When the client attribute cache
> times out, the folder link icon returns to the normal folder icon.  The
> here-again-gone-again nature of the folder link icon may be surprising to the
> user, or may be useful to communicate a resolution and/or automount status.  We
> don't know if this happens on other operating systems and file managers.
> ---
>  source3/include/vfs.h  |  1 +
>  source3/smbd/dosmode.c | 24 +++++++++++++++++++++++-
>  source3/smbd/service.c |  1 +
>  source3/smbd/trans2.c  | 21 ++++++++++++++++++---
>  4 files changed, 43 insertions(+), 4 deletions(-)
> 
> diff --git a/source3/include/vfs.h b/source3/include/vfs.h
> index d71d7cc4483..652986ca1a4 100644
> --- a/source3/include/vfs.h
> +++ b/source3/include/vfs.h
> @@ -467,6 +467,7 @@ typedef struct connection_struct {
>  	/* Device number of the directory of the share mount.
>  	   Used to ensure unique FileIndex returns. */
>  	SMB_DEV_T base_share_dev;
> +	SMB_DEV_T curr_share_dev;
> 
>  	name_compare_entry *hide_list; /* Per-share list of files to return as hidden. */
>  	name_compare_entry *veto_list; /* Per-share list of files to veto (never show). */
> diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
> index 3181f2e78a9..8274e4f1bc1 100644
> --- a/source3/smbd/dosmode.c
> +++ b/source3/smbd/dosmode.c
> @@ -65,7 +65,9 @@ static void dos_mode_debug_print(const char *func, uint32_t mode)
>  	if (mode & FILE_ATTRIBUTE_COMPRESSED) {
>  		fstrcat(modestr, "[compressed]");
>  	}
> -
> +	if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
> +		fstrcat(modestr, "[reparse point]");
> +	}
>  	DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
>  		 modestr);
>  }
> @@ -247,6 +249,13 @@ static uint32_t dos_mode_from_sbuf(connection_struct *conn,
> 
>  	if (S_ISDIR(smb_fname->st.st_ex_mode))
>  		result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
> +
> +	if (conn->base_share_dev != smb_fname->st.st_ex_dev){
> +		if(conn->curr_share_dev != smb_fname->st.st_ex_dev){
> +			result |= FILE_ATTRIBUTE_REPARSE_POINT;
> +			conn->curr_share_dev = smb_fname->st.st_ex_dev;
> +		}
> +	}
> 
>  	result |= set_link_read_only_flag(&smb_fname->st);
> 
> @@ -395,6 +404,12 @@ NTSTATUS get_ea_dos_attribute(connection_struct *conn,
>  	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
>  		dosattr |= FILE_ATTRIBUTE_DIRECTORY;
>  	}
> +	if (conn->base_share_dev != smb_fname->st.st_ex_dev){
> +		if(conn->curr_share_dev != smb_fname->st.st_ex_dev){
> +			dosattr |= FILE_ATTRIBUTE_REPARSE_POINT;
> +			conn->curr_share_dev = smb_fname->st.st_ex_dev;
> +		}
> +	}
>  	/* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
>  	*pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
> 
> @@ -682,6 +697,13 @@ uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
>  	} else if (result == 0) {
>  		result = FILE_ATTRIBUTE_NORMAL;
>  	}
> +
> +	if (conn->base_share_dev != smb_fname->st.st_ex_dev){
> +		if(conn->curr_share_dev != smb_fname->st.st_ex_dev){
> +			result |= FILE_ATTRIBUTE_REPARSE_POINT;
> +			conn->curr_share_dev = smb_fname->st.st_ex_dev;
> +		}
> +	}
> 
>  	result = filter_mode_by_protocol(result);
> 
> diff --git a/source3/smbd/service.c b/source3/smbd/service.c
> index 75a47dee0ca..52a14f8f92d 100644
> --- a/source3/smbd/service.c
> +++ b/source3/smbd/service.c
> @@ -821,6 +821,7 @@ static NTSTATUS make_connection_snum(struct smbXsrv_connection *xconn,
>  		goto err_root_exit;
>  	}
>  	conn->base_share_dev = smb_fname_cpath->st.st_ex_dev;
> +	conn->curr_share_dev = conn->base_share_dev;
> 
>  	talloc_free(conn->origpath);
>  	conn->origpath = talloc_strdup(conn, conn->connectpath);
> diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
> index de6073a973f..f1a17635d27 100644
> --- a/source3/smbd/trans2.c
> +++ b/source3/smbd/trans2.c
> @@ -2012,7 +2012,12 @@ static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
>  		SIVAL(p,0,mode); p += 4;
>  		q = p; p += 4; /* q is placeholder for name length. */
>  		if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
> -			SIVAL(p, 0, IO_REPARSE_TAG_DFS);
> +			if(conn->base_share_dev != conn->curr_share_dev){
> +				SIVAL(p, 0, IO_REPARSE_TAG_MOUNT_POINT);
> +			}
> +                        else {
> +				SIVAL(p, 0, IO_REPARSE_TAG_DFS);
> +			}
>  		} else {
>  			unsigned int ea_size = estimate_ea_size(conn, NULL,
>  								smb_fname);
> @@ -2206,7 +2211,12 @@ static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
>  		SIVAL(p,0,mode); p += 4;
>  		q = p; p += 4; /* q is placeholder for name length. */
>  		if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
> -			SIVAL(p, 0, IO_REPARSE_TAG_DFS);
> +			if(conn->base_share_dev != conn->curr_share_dev){
> +				SIVAL(p, 0, IO_REPARSE_TAG_MOUNT_POINT);
> +			}
> +                        else {
> +				SIVAL(p, 0, IO_REPARSE_TAG_DFS);
> +			}
>  		} else {
>  			unsigned int ea_size = estimate_ea_size(conn, NULL,
>  								smb_fname);
> @@ -2257,7 +2267,12 @@ static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
>  		SIVAL(p,0,mode); p += 4;
>  		q = p; p += 4; /* q is placeholder for name length */
>  		if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
> -			SIVAL(p, 0, IO_REPARSE_TAG_DFS);
> +			if(conn->base_share_dev != conn->curr_share_dev){
> +				SIVAL(p, 0, IO_REPARSE_TAG_MOUNT_POINT);
> +			}
> +                        else {
> +				SIVAL(p, 0, IO_REPARSE_TAG_DFS);
> +			}
>  		} else if (readdir_attr_data &&
>  			   readdir_attr_data->type == RDATTR_AAPL) {
>  			/*
> --
> 2.11.0
> 




More information about the samba-technical mailing list