r640: Make cifsvfs symlinks work with paths within and external to the
authorJeremy Allison <jra@samba.org>
Mon, 10 May 2004 23:04:31 +0000 (23:04 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:51:31 +0000 (10:51 -0500)
share. Store external paths prefixed with smbln:.
Jeremy.

source/include/trans2.h
source/smbd/trans2.c

index 3106cd092ab018505031d2c429cecb371218b92b..98a4fc09768fbc1e01990d7c43f62f6baedd4329 100644 (file)
@@ -450,6 +450,7 @@ Offset Size         Name
 #define CIFS_UNIX_FCNTL_LOCKS_CAP           0x1
 #define CIFS_UNIX_POSIX_ACLS_CAP            0x2
 
+#define CIFS_CLIENT_SYMLINK_STRING     "smbln:"
 /* ... more as we think of them :-). */
 
 #endif
index 25954d44336689bee4c9d1f4d06b5e38aa041945..825bae86bb953dfa6e34eea251d3155dce6d96b7 100644 (file)
@@ -2762,6 +2762,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                case SMB_QUERY_FILE_UNIX_LINK:
                        {
                                pstring buffer;
+                               char *bufp = buffer;
 
                                DEBUG(10,("call_trans2qfilepathinfo: SMB_QUERY_FILE_UNIX_LINK\n"));
 #ifdef S_ISLNK
@@ -2774,7 +2775,11 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                                if (len == -1)
                                        return(UNIXERROR(ERRDOS,ERRnoaccess));
                                buffer[len] = 0;
-                               len = srvstr_push(outbuf, pdata, buffer, -1, STR_TERMINATE);
+                               if (strncmp(buffer, CIFS_CLIENT_SYMLINK_STRING, strlen(CIFS_CLIENT_SYMLINK_STRING)) == 0) {
+                                       bufp += strlen(CIFS_CLIENT_SYMLINK_STRING);
+                               }
+
+                               len = srvstr_push(outbuf, pdata, bufp, -1, STR_TERMINATE);
                                pdata += len;
                                data_size = PTR_DIFF(pdata,(*ppdata));
 
@@ -2885,7 +2890,7 @@ static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in
        /* Store the UNIX converted path. */
        pstrcpy(link_dest_out, link_dest);
 
-       p = strrchr(link_dest, '/');
+       p = strrchr_m(link_dest, '/');
        if (p) {
                fstrcpy(last_component, p+1);
                *p = '\0';
@@ -2897,6 +2902,9 @@ static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in
        if (SMB_VFS_REALPATH(conn,link_dest,resolved_name) == NULL)
                return -1;
 
+       DEBUG(10,("ensure_link_is_safe: realpath: link_dest (%s) -> real name (%s)\n",
+                       link_dest, resolved_name ));
+
        pstrcpy(link_dest, resolved_name);
        pstrcat(link_dest, "/");
        pstrcat(link_dest, last_component);
@@ -2910,6 +2918,9 @@ static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in
                pstrcpy(link_test, link_dest);
        }
 
+       DEBUG(10,("ensure_link_is_safe: connectpath = %s, absolute resolved path = %s\n",
+               conn->connectpath, link_test ));
+
        /*
         * Check if the link is within the share.
         */
@@ -3483,6 +3494,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                {
                        pstring oldname;
                        char *newname = fname;
+                       BOOL cifs_client_link = False;
 
                        /* Set a symbolic link. */
                        /* Don't allow this if follow links is false. */
@@ -3490,13 +3502,35 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                        if (!lp_symlinks(SNUM(conn)))
                                return(ERROR_DOS(ERRDOS,ERRnoaccess));
 
-                       srvstr_get_path(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE, &status);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               return ERROR_NT(status);
+                       srvstr_pull(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE);
+                       unix_format(oldname);
+
+                       if (*oldname == '/') {
+                               /* Absolute paths are automatically a client resolved link. */
+                               cifs_client_link = True;
+                       } else {
+                               pstring rel_name;
+                               char *last_dirp = NULL;
+
+                               pstrcpy(rel_name, newname);
+                               last_dirp = strrchr_m(rel_name, '/');
+                               if (last_dirp) {
+                                       last_dirp[1] = '\0';
+                               } else {
+                                       pstrcpy(rel_name, "./");
+                               }
+                               pstrcat(rel_name, oldname);
+                               if (ensure_link_is_safe(conn, rel_name, rel_name) != 0)
+                                       cifs_client_link = True;
+
                        }
 
-                       if (ensure_link_is_safe(conn, oldname, oldname) != 0)
-                               return(UNIXERROR(ERRDOS,ERRnoaccess));
+                       if (cifs_client_link) {
+                               pstring tmp_name;
+                               pstrcpy(tmp_name, CIFS_CLIENT_SYMLINK_STRING);
+                               pstrcat(tmp_name, oldname);
+                               pstrcpy(oldname, tmp_name);
+                       }
 
                        DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
                                fname, oldname ));