shadow_copy2 requires wide links enabled

Paul B. Henson henson at acm.org
Mon Jan 10 20:14:42 MST 2011


On Fri, 7 Jan 2011, Paul B. Henson wrote:

> An initial wild guess is that maybe something somewhere assumes that
> shadow copy paths on the underlying filesystem are always in the @GMT
> format.

Well, my initial wild guess seems to be correct. The code causing the
failure is in smbd/vfs.c:

                    if (strncmp(conn_rootdir, resolved_name,
                                strlen(conn_rootdir)) != 0) {
                            DEBUG(0, ("check_reduced_name: Bad access "
                                      "attempt: %s is a symlink outside the "
                                      "share path\n", fname));
                            if (free_resolved_name) {
                                    SAFE_FREE(resolved_name);
                            }
                            return NT_STATUS_ACCESS_DENIED;
                   }

I added a couple extra debugs to see what conn_rootdir and resolved_name
were:

[2011/01/10 18:03:48.246724,  0, pid=26495]
smbd/vfs.c:965(check_reduced_name)
  check_reduced_name: conn_rootdir =
/export/user/henson/.zfs/snapshot/@GMT-2011.01.08-02.06.22/

[2011/01/10 18:03:48.246780,  0, pid=26495]
smbd/vfs.c:966(check_reduced_name)
  check_reduced_name: resolved_name =
/export/user/henson/.zfs/snapshot/backup-2011.01.07-18.06.22/license

[2011/01/10 18:03:48.246828,  0, pid=26495]
smbd/vfs.c:967(check_reduced_name)
  check_reduced_name: Bad access attempt: @GMT-2011.01.08-02.06.22/license
is a symlink outside the share path

Internally, samba thinks the root directory has the magic @GMT name, but on
the actual filesystem the path uses the format specification I defined
which doesn't match.

It's the shadow_copy2 module that seems to be doing it:

[2011/01/10 18:14:35.269415,  0, pid=27128]
modules/vfs_shadow_copy2.c:681(shadow_copy2_connectpath)
  shadow_copy2_connectpath called with @GMT-2011.01.11-02.06.15/license

[2011/01/10 18:14:35.269537,  0, pid=27128]
modules/vfs_shadow_copy2.c:729(shadow_copy2_connectpath)
  shadow_copy2_connectpath: '@GMT-2011.01.11-02.06.15/license' ->
'/export/user/henson/.zfs/snapshot/@GMT-2011.01.11-02.06.15/'

Interestingly, there's another function, convert_shadow2_name, which seems
to convert a magic @GMT path into the actual underlying filesystem path:

[2011/01/10 18:33:32.454893,  0, pid=28381]
modules/vfs_shadow_copy2.c:485(convert_shadow2_name)
convert_shadow2_name: '@GMT-2011.01.11-02.06.15/license' ->
'/export/user/henson/.zfs/snapshot/backup-2011.01.10-1
8.06.15//license'

It seems if shadow_copy2_connectpath did this then it wouldn't be
misclassified as a wide link.

With minimal understanding of most of the internal workings of samba ;), I
modified shadow_copy2_connectpath to call convert_shadow2_name so it would
return actual filesystem paths rather than magic @GMT paths.

This initially didn't work, as convert_shadow2_name resulted in a double /:

[2011/01/10 18:51:35.891642,  0, pid=29609]
modules/vfs_shadow_copy2.c:484(convert_shadow2_name)
  convert_shadow2_name: '@GMT-2011.01.11-02.06.15/license' ->
'/export/user/henson/.zfs/snapshot/backup-2011.01.10-1
8.06.15//license'

Resulting in
/export/user/henson/.zfs/snapshot/backup-2011.01.10-18.06.15/license still
not being underneath
/export/user/henson/.zfs/snapshot/backup-2011.01.10-18.06.15//license

It seems baseoffset was the empty string. I made a minor tweak to
convert_shadow2_name as not to result in '//' when baseoffset is empty (the
first way that came to mind, not necessarily the cleanest or best way), and
woo-hoo, shadow copies worked with wide links disabled.

Here's the diff to vfs_shadow_copy2.c:

---------------------------------------------------------------------
*** vfs_shadow_copy2.c.orig     Mon Jan 10 18:13:03 2011
--- vfs_shadow_copy2.c  Mon Jan 10 19:06:38 2011
***************
*** 476,485 ****
        if (*relpath == '/') relpath++;
        if (*baseoffset == '/') baseoffset++;

!       ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
                              snapdir,
                              snapshot,
!                             baseoffset,
                              relpath);
        DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
        talloc_free(tmp_ctx);
--- 476,486 ----
        if (*relpath == '/') relpath++;
        if (*baseoffset == '/') baseoffset++;

!       ret = talloc_asprintf(handle->data, "%s/%s%s%s/%s",
                              snapdir,
                              snapshot,
!                             *baseoffset ? "/" : "",
!                             baseoffset,
                              relpath);
        DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
        talloc_free(tmp_ctx);
***************
*** 690,731 ****
                return NULL;
        }

!       snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
!       if (snapdir == NULL) {
!               DEBUG(2,("no snapdir found for share at %s\n",
!                        handle->conn->connectpath));
!               TALLOC_FREE(tmp_ctx);
!               return NULL;
!       }
!
!       basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
!       if (basedir == NULL) {
!               DEBUG(2,("no basedir found for share at %s\n",
!                        handle->conn->connectpath));
!               TALLOC_FREE(tmp_ctx);
!               return NULL;
!       }
!
!       baselen = strlen(basedir);
!       baseoffset = handle->conn->connectpath + baselen;
!
!       /* some sanity checks */
!       if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
!           (handle->conn->connectpath[baselen] != 0
!            && handle->conn->connectpath[baselen] != '/')) {
!               DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
!                        "parent of %s\n", basedir,
!                        handle->conn->connectpath));
!               TALLOC_FREE(tmp_ctx);
!               return NULL;
!       }
!
!       if (*baseoffset == '/') baseoffset++;
!
!       ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
!                             snapdir,
!                             GMT_NAME_LEN, fname,
!                             baseoffset);
        DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
        TALLOC_FREE(tmp_ctx);
        return ret;
--- 691,697 ----
                return NULL;
        }

!       ret = convert_shadow2_name(handle, fname, gmt_start);
        DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
        TALLOC_FREE(tmp_ctx);
        return ret;
---------------------------------------------------------------------

It's quite possible I've broken something else horribly by making this
change :), but at least I think I've found the underlying problem, and if
this isn't the way to fix it perhaps someone with a better understanding
than I might have a suggestion. So far from my initial limited testing
other than shadow copies working now with wide links disabled everything
else seems the same.

Thanks...


-- 
Paul B. Henson  |  (909) 979-6361  |  http://www.csupomona.edu/~henson/
Operating Systems and Network Analyst  |  henson at csupomona.edu
California State Polytechnic University  |  Pomona CA 91768


More information about the samba-technical mailing list