ffmpeg中samba網(wǎng)絡協(xié)議的兼容分析(三)

之前我們發(fā)現(xiàn)回調函數(shù)my_smbc_get_auth_data_with_context_fn內能獲取到用戶名和工作組等蝇庭,并且都是我設置的值懦冰,在這個回調中打個斷點看看,我們再次調試

image

看到左側的調用棧很澄,紅框中的幾個函數(shù)都是libsmbclient的源碼內的呻引,我們查找samba源碼(4.0.26版本):

int smbc_open(const char *furl,
          int flags,
          mode_t mode)
{
    SMBCFILE * file;
    int fd;

        file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode);
    if (!file)
        return -1;

    fd = add_fd(file);
    if (fd == -1) 
                smbc_getFunctionClose(statcont)(statcont, file);
    return fd;
}

smbc_open_fn smbc_getFunctionOpen(SMBCCTX *c)
{
        return c->open;
}

這里的smbc_open_fn其實是這樣的:

typedef SMBCFILE * (*smbc_open_fn)(SMBCCTX *c,
                                   const char *fname,
                                   int flags,
                                   mode_t mode);

smbc_open_fn smbc_getFunctionOpen(SMBCCTX *c);

void smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn);

而SMBCCTX為

typedef struct _SMBCCTX SMBCCTX;

struct _SMBCCTX
{
        /**
         * debug level
         *
         * DEPRECATED:
         * Use smbc_getDebug() and smbc_setDebug()
         */
        int     debug DEPRECATED_SMBC_INTERFACE;
    
        /**
         * netbios name used for making connections
         *
         * DEPRECATED:
         * Use smbc_getNetbiosName() and smbc_setNetbiosName()
         */
        char * netbios_name DEPRECATED_SMBC_INTERFACE;

        /**
         * workgroup name used for making connections
         *
         * DEPRECATED:
         * Use smbc_getWorkgroup() and smbc_setWorkgroup()
         */
        char * workgroup DEPRECATED_SMBC_INTERFACE;

        /**
         * username used for making connections
         *
         * DEPRECATED:
         * Use smbc_getUser() and smbc_setUser()
         */
        char * user DEPRECATED_SMBC_INTERFACE;

        /**
         * timeout used for waiting on connections / response data (in
         * milliseconds)
         *
         * DEPRECATED:
         * Use smbc_getTimeout() and smbc_setTimeout()
         */
        int timeout DEPRECATED_SMBC_INTERFACE;

    /**
         * callable functions for files:
     * For usage and return values see the SMBC_* functions
         *
         * DEPRECATED:
         *
         * Use smbc_getFunction*() and smbc_setFunction*(), e.g.
         * smbc_getFunctionOpen(), smbc_setFunctionUnlink(), etc.
     */ 
        smbc_open_fn                    open DEPRECATED_SMBC_INTERFACE;
        smbc_creat_fn                   creat DEPRECATED_SMBC_INTERFACE;
        smbc_read_fn                    read DEPRECATED_SMBC_INTERFACE;
        smbc_write_fn                   write DEPRECATED_SMBC_INTERFACE;
        smbc_unlink_fn                  unlink DEPRECATED_SMBC_INTERFACE;
        smbc_rename_fn                  rename DEPRECATED_SMBC_INTERFACE;
        smbc_lseek_fn                   lseek DEPRECATED_SMBC_INTERFACE;
        smbc_stat_fn                    stat DEPRECATED_SMBC_INTERFACE;
        smbc_fstat_fn                   fstat DEPRECATED_SMBC_INTERFACE;
#if 0 /* internal */
        smbc_ftruncate_fn               ftruncate_fn;
#endif
        smbc_close_fn                   close_fn DEPRECATED_SMBC_INTERFACE;
        smbc_opendir_fn                 opendir DEPRECATED_SMBC_INTERFACE;
        smbc_closedir_fn                closedir DEPRECATED_SMBC_INTERFACE;
        smbc_readdir_fn                 readdir DEPRECATED_SMBC_INTERFACE;
        smbc_getdents_fn                getdents DEPRECATED_SMBC_INTERFACE;
        smbc_mkdir_fn                   mkdir DEPRECATED_SMBC_INTERFACE;
        smbc_rmdir_fn                   rmdir DEPRECATED_SMBC_INTERFACE;
        smbc_telldir_fn                 telldir DEPRECATED_SMBC_INTERFACE;
        smbc_lseekdir_fn                lseekdir DEPRECATED_SMBC_INTERFACE;
        smbc_fstatdir_fn                fstatdir DEPRECATED_SMBC_INTERFACE;
        smbc_chmod_fn                   chmod DEPRECATED_SMBC_INTERFACE;
        smbc_utimes_fn                  utimes DEPRECATED_SMBC_INTERFACE;
        smbc_setxattr_fn                setxattr DEPRECATED_SMBC_INTERFACE;
        smbc_getxattr_fn                getxattr DEPRECATED_SMBC_INTERFACE;
        smbc_removexattr_fn             removexattr DEPRECATED_SMBC_INTERFACE;
        smbc_listxattr_fn               listxattr DEPRECATED_SMBC_INTERFACE;

        /* Printing-related functions */
        smbc_print_file_fn              print_file DEPRECATED_SMBC_INTERFACE;
        smbc_open_print_job_fn          open_print_job DEPRECATED_SMBC_INTERFACE;
        smbc_list_print_jobs_fn         list_print_jobs DEPRECATED_SMBC_INTERFACE;
        smbc_unlink_print_job_fn        unlink_print_job DEPRECATED_SMBC_INTERFACE;

        /*
        ** Callbacks
        *
        * DEPRECATED:
        *
        * See the comment above each field, for the getter and setter
        * functions that should now be used.
        */
    struct _smbc_callbacks
        {
        /**
                 * authentication function callback: called upon auth requests
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionAuthData(), smbc_setFunctionAuthData()
         */
                smbc_get_auth_data_fn auth_fn DEPRECATED_SMBC_INTERFACE;
        
        /**
                 * check if a server is still good
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionCheckServer(),
                 * smbc_setFunctionCheckServer()
         */
        smbc_check_server_fn check_server_fn DEPRECATED_SMBC_INTERFACE;

        /**
                 * remove a server if unused
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionRemoveUnusedServer(),
                 * smbc_setFunctionCheckServer()
         */
        smbc_remove_unused_server_fn remove_unused_server_fn DEPRECATED_SMBC_INTERFACE;

        /** Cache subsystem
                 *
         * For an example cache system see
         * samba/source/libsmb/libsmb_cache.c
                 *
                 * Cache subsystem * functions follow.
         */

        /**
                 * server cache addition 
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionAddCachedServer(),
                 * smbc_setFunctionAddCachedServer()
         */
        smbc_add_cached_srv_fn add_cached_srv_fn DEPRECATED_SMBC_INTERFACE;

        /**
                 * server cache lookup 
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionGetCachedServer(),
                 * smbc_setFunctionGetCachedServer()
         */
        smbc_get_cached_srv_fn get_cached_srv_fn DEPRECATED_SMBC_INTERFACE;

        /**
                 * server cache removal
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionRemoveCachedServer(),
                 * smbc_setFunctionRemoveCachedServer()
         */
        smbc_remove_cached_srv_fn remove_cached_srv_fn DEPRECATED_SMBC_INTERFACE;
        
        /**
                 * server cache purging, try to remove all cached servers
                 * (disconnect)
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionPurgeCachedServers(),
                 * smbc_setFunctionPurgeCachedServers()
         */
        smbc_purge_cached_fn purge_cached_fn DEPRECATED_SMBC_INTERFACE;
    } callbacks;

        /**
         * Space where the private data of the server cache used to be
         *
         * DEPRECATED:
         * Use smbc_getServerCacheData(), smbc_setServerCacheData()
         */
        void * reserved DEPRECATED_SMBC_INTERFACE;

        /*
         * Very old configuration options.
         * 
         * DEPRECATED:
         * Use one of the following functions instead:
         *   smbc_setOptionUseKerberos()
         *   smbc_getOptionUseKerberos()
         *   smbc_setOptionFallbackAfterKerberos()
         *   smbc_getOptionFallbackAfterKerberos()
         *   smbc_setOptionNoAutoAnonymousLogin()
         *   smbc_getOptionNoAutoAnonymousLogin()
         */
        int flags DEPRECATED_SMBC_INTERFACE;
    
        /**
         * user options selections that apply to this session
         *
         * NEW OPTIONS ARE NOT ADDED HERE!
         *
         * DEPRECATED:
         * To set and retrieve options, use the smbc_setOption*() and
         * smbc_getOption*() functions.
         */
        struct _smbc_options {
                int browse_max_lmb_count DEPRECATED_SMBC_INTERFACE;
                int urlencode_readdir_entries DEPRECATED_SMBC_INTERFACE;
                int one_share_per_server DEPRECATED_SMBC_INTERFACE;
        } options DEPRECATED_SMBC_INTERFACE;
    
    /** INTERNAL DATA
     * do _NOT_ touch this from your program !
     */
    struct SMBC_internal_data * internal;
};

通過上面代碼看出c->open,_SMBCCTX的open的類型就是smbc_open_fn

而statcont是一個SMBCCTX*的全局變量祸憋,在這里初始化了

static SMBCCTX * statcont = NULL;

int smbc_init(smbc_get_auth_data_fn fn,
          int debug)
{
    if (!smbc_compat_initialized) {
        statcont = smbc_new_context();
        if (!statcont) 
            return -1;

                smbc_setDebug(statcont, debug);
                smbc_setFunctionAuthData(statcont, fn);

        if (!smbc_init_context(statcont)) {
            smbc_free_context(statcont, False);
            return -1;
        }

        smbc_compat_initialized = 1;

        return 0;
    }
    return 0;
}

所以smbc_getFunctionOpen(statcont)() == statcont->open()

所以会宪,file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode)實質就是執(zhí)行了smbc_open_fn()

在smbc_open_fn申明的下方有個這個:

void smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn);

源碼為:

void
smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn)
{
        c->open = fn;
}

從這個smbc_setFunctionOpen可以推斷,此函數(shù)是為SMBCCTX的open成員賦值用的

果不其然蚯窥,在smbc_new_context里的確設置了這一open成員

/*
 * Get a new empty handle to fill in with your own info
 */
SMBCCTX *
smbc_new_context(void)
{
        SMBCCTX *context;
    TALLOC_CTX *frame = talloc_stackframe();

        /* The first call to this function should initialize the module */
        SMB_THREAD_ONCE(&SMBC_initialized, SMBC_module_init, NULL);

        /*
         * All newly added context fields should be placed in
         * SMBC_internal_data, not directly in SMBCCTX.
         */
        context = SMB_MALLOC_P(SMBCCTX);
        if (!context) {
        TALLOC_FREE(frame);
                errno = ENOMEM;
                return NULL;
        }

        ZERO_STRUCTP(context);

        context->internal = SMB_MALLOC_P(struct SMBC_internal_data);
        if (!context->internal) {
        TALLOC_FREE(frame);
                SAFE_FREE(context);
                errno = ENOMEM;
                return NULL;
        }

        /* Initialize the context and establish reasonable defaults */
        ZERO_STRUCTP(context->internal);

        smbc_setDebug(context, 0);
        smbc_setTimeout(context, 20000);

        smbc_setOptionFullTimeNames(context, False);
        smbc_setOptionOpenShareMode(context, SMBC_SHAREMODE_DENY_NONE);
        smbc_setOptionSmbEncryptionLevel(context, SMBC_ENCRYPTLEVEL_NONE);
        smbc_setOptionUseCCache(context, True);
        smbc_setOptionCaseSensitive(context, False);
        smbc_setOptionBrowseMaxLmbCount(context, 3);    /* # LMBs to query */
        smbc_setOptionUrlEncodeReaddirEntries(context, False);
        smbc_setOptionOneSharePerServer(context, False);
    if (getenv("LIBSMBCLIENT_NO_CCACHE") == NULL) {
        smbc_setOptionUseCCache(context, true);
    }

        smbc_setFunctionAuthData(context, SMBC_get_auth_data);
        smbc_setFunctionCheckServer(context, SMBC_check_server);
        smbc_setFunctionRemoveUnusedServer(context, SMBC_remove_unused_server);

        smbc_setOptionUserData(context, NULL);
        smbc_setFunctionAddCachedServer(context, SMBC_add_cached_server);
        smbc_setFunctionGetCachedServer(context, SMBC_get_cached_server);
        smbc_setFunctionRemoveCachedServer(context, SMBC_remove_cached_server);
        smbc_setFunctionPurgeCachedServers(context, SMBC_purge_cached_servers);

        smbc_setFunctionOpen(context, SMBC_open_ctx);
        smbc_setFunctionCreat(context, SMBC_creat_ctx);
        smbc_setFunctionRead(context, SMBC_read_ctx);
        smbc_setFunctionWrite(context, SMBC_write_ctx);
        smbc_setFunctionClose(context, SMBC_close_ctx);
        smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
        smbc_setFunctionRename(context, SMBC_rename_ctx);
        smbc_setFunctionLseek(context, SMBC_lseek_ctx);
        smbc_setFunctionFtruncate(context, SMBC_ftruncate_ctx);
        smbc_setFunctionStat(context, SMBC_stat_ctx);
        smbc_setFunctionStatVFS(context, SMBC_statvfs_ctx);
        smbc_setFunctionFstatVFS(context, SMBC_fstatvfs_ctx);
        smbc_setFunctionFstat(context, SMBC_fstat_ctx);
        smbc_setFunctionOpendir(context, SMBC_opendir_ctx);
        smbc_setFunctionClosedir(context, SMBC_closedir_ctx);
        smbc_setFunctionReaddir(context, SMBC_readdir_ctx);
        smbc_setFunctionGetdents(context, SMBC_getdents_ctx);
        smbc_setFunctionMkdir(context, SMBC_mkdir_ctx);
        smbc_setFunctionRmdir(context, SMBC_rmdir_ctx);
        smbc_setFunctionTelldir(context, SMBC_telldir_ctx);
        smbc_setFunctionLseekdir(context, SMBC_lseekdir_ctx);
        smbc_setFunctionFstatdir(context, SMBC_fstatdir_ctx);
        smbc_setFunctionChmod(context, SMBC_chmod_ctx);
        smbc_setFunctionUtimes(context, SMBC_utimes_ctx);
        smbc_setFunctionSetxattr(context, SMBC_setxattr_ctx);
        smbc_setFunctionGetxattr(context, SMBC_getxattr_ctx);
        smbc_setFunctionRemovexattr(context, SMBC_removexattr_ctx);
        smbc_setFunctionListxattr(context, SMBC_listxattr_ctx);

        smbc_setFunctionOpenPrintJob(context, SMBC_open_print_job_ctx);
        smbc_setFunctionPrintFile(context, SMBC_print_file_ctx);
        smbc_setFunctionListPrintJobs(context, SMBC_list_print_jobs_ctx);
        smbc_setFunctionUnlinkPrintJob(context, SMBC_unlink_print_job_ctx);

    TALLOC_FREE(frame);
        return context;
}

通過下面這句代碼掸鹅,我們可以定位到SMBC_open_ctx

smbc_setFunctionOpen(context, SMBC_open_ctx);

SMBC_open_ctx塞帐,正是紅色方框調用棧中的其中一個小伙伴

我們來看下它的源碼:

SMBCFILE * SMBC_open_ctx(SMBCCTX *context,
              const char *fname,
              int flags,
              mode_t mode)
{
    char *server = NULL;
        char *share = NULL;
        char *user = NULL;
        char *password = NULL;
        char *workgroup = NULL;
    char *path = NULL;
    char *targetpath = NULL;
    struct cli_state *targetcli = NULL;
    SMBCSRV *srv   = NULL;
    SMBCFILE *file = NULL;
    uint16_t fd;
    NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
    TALLOC_CTX *frame = talloc_stackframe();

    if (!context || !context->internal->initialized) {
        errno = EINVAL;  /* Best I can think of ... */
        TALLOC_FREE(frame);
        return NULL;
    }

    if (!fname) {
        errno = EINVAL;
        TALLOC_FREE(frame);
        return NULL;
    }

    if (SMBC_parse_path(frame,
                            context,
                            fname,
                            &workgroup,
                            &server,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
        errno = EINVAL;
        TALLOC_FREE(frame);
        return NULL;
        }

    if (!user || user[0] == (char)0) {
        user = talloc_strdup(frame, smbc_getUser(context));
        if (!user) {
                    errno = ENOMEM;
            TALLOC_FREE(frame);
            return NULL;
        }
    }

    srv = SMBC_server(frame, context, True,
                          server, share, &workgroup, &user, &password);
    if (!srv) {
        if (errno == EPERM) errno = EACCES;
        TALLOC_FREE(frame);
        return NULL;  /* SMBC_server sets errno */
    }

    /* Hmmm, the test for a directory is suspect here ... FIXME */

    if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
        status = NT_STATUS_OBJECT_PATH_INVALID;
    } else {
        file = SMB_MALLOC_P(SMBCFILE);
        if (!file) {
            errno = ENOMEM;
            TALLOC_FREE(frame);
            return NULL;
        }

        ZERO_STRUCTP(file);

        /*d_printf(">>>open: resolving %s\n", path);*/
        status = cli_resolve_path(
            frame, "", context->internal->auth_info,
            srv->cli, path, &targetcli, &targetpath);
        if (!NT_STATUS_IS_OK(status)) {
            d_printf("Could not resolve %s\n", path);
                        errno = ENOENT;
            SAFE_FREE(file);
            TALLOC_FREE(frame);
            return NULL;
        }
        /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/

        status = cli_open(targetcli, targetpath, flags,
                                   context->internal->share_mode, &fd);
        if (!NT_STATUS_IS_OK(status)) {

            /* Handle the error ... */

            SAFE_FREE(file);
            errno = SMBC_errno(context, targetcli);
            TALLOC_FREE(frame);
            return NULL;
        }

        /* Fill in file struct */

        file->cli_fd  = fd;
        file->fname   = SMB_STRDUP(fname);
        file->srv     = srv;
        file->offset  = 0;
        file->file    = True;

        DLIST_ADD(context->internal->files, file);

                /*
                 * If the file was opened in O_APPEND mode, all write
                 * operations should be appended to the file.  To do that,
                 * though, using this protocol, would require a getattrE()
                 * call for each and every write, to determine where the end
                 * of the file is. (There does not appear to be an append flag
                 * in the protocol.)  Rather than add all of that overhead of
                 * retrieving the current end-of-file offset prior to each
                 * write operation, we'll assume that most append operations
                 * will continuously write, so we'll just set the offset to
                 * the end of the file now and hope that's adequate.
                 *
                 * Note to self: If this proves inadequate, and O_APPEND
                 * should, in some cases, be forced for each write, add a
                 * field in the context options structure, for
                 * "strict_append_mode" which would select between the current
                 * behavior (if FALSE) or issuing a getattrE() prior to each
                 * write and forcing the write to the end of the file (if
                 * TRUE).  Adding that capability will likely require adding
                 * an "append" flag into the _SMBCFILE structure to track
                 * whether a file was opened in O_APPEND mode.  -- djl
                 */
                if (flags & O_APPEND) {
                        if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
                                (void) SMBC_close_ctx(context, file);
                                errno = ENXIO;
                TALLOC_FREE(frame);
                                return NULL;
                        }
                }

        TALLOC_FREE(frame);
        return file;
    }

    /* Check if opendir needed ... */

    if (!NT_STATUS_IS_OK(status)) {
        int eno = 0;

        eno = SMBC_errno(context, srv->cli);
        file = smbc_getFunctionOpendir(context)(context, fname);
        if (!file) errno = eno;
        TALLOC_FREE(frame);
        return file;
    }

    errno = EINVAL; /* FIXME, correct errno ? */
    TALLOC_FREE(frame);
    return NULL;
}

從函數(shù)的參數(shù)類型和返回類型就可以斷定,這正是我們所希望的SMBC_open_ctx(符合預期)

再次看到紅框中調用棧巍沙,的確是smbc_open_ctx調用了smbc_server葵姥,也符合代碼中寫得那樣

srv = SMBC_server(frame, context, True,
                          server, share, &workgroup, &user, &password);

我們先找到smbc_server的源碼

SMBCSRV *
SMBC_server(TALLOC_CTX *ctx,
        SMBCCTX *context,
        bool connect_if_not_found,
        const char *server,
        const char *share,
        char **pp_workgroup,
        char **pp_username,
        char **pp_password)
{
    SMBCSRV *srv=NULL;
    bool in_cache = false;

    srv = SMBC_server_internal(ctx, context, connect_if_not_found,
            server, share, pp_workgroup,
            pp_username, pp_password, &in_cache);

    if (!srv) {
        return NULL;
    }
    if (in_cache) {
        return srv;
    }

    /* Now add it to the cache (internal or external)  */
    /* Let the cache function set errno if it wants to */
    errno = 0;
    if (smbc_getFunctionAddCachedServer(context)(context, srv,
                        server, share,
                        *pp_workgroup,
                        *pp_username)) {
        int saved_errno = errno;
        DEBUG(3, (" Failed to add server to cache\n"));
        errno = saved_errno;
        if (errno == 0) {
            errno = ENOMEM;
        }
        SAFE_FREE(srv);
        return NULL;
    }

    DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
        server, share, srv));

    DLIST_ADD(context->internal->servers, srv);
    return srv;
}

發(fā)現(xiàn)此函數(shù)又一次符合預期(紅框)的調用了SMBC_server_internal函數(shù),此時句携,內心會問:這里的password和username等直接就從smbc_server傳入了SMBC_server_internal榔幸,那么password和username等信息是不是在SMBC_server_internal里面才處理的呢?

為了方便閱讀矮嫉,我就不一一去貼出下面將要調用的SMBC_find_server的代碼了削咆,直接說答案:并沒有!

其實是在SMBC_parse_path進行處理的(smbc_open_ctx中的)

if (SMBC_parse_path(frame,
                            context,
                            fname,
                            &workgroup,
                            &server,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
        errno = EINVAL;
        TALLOC_FREE(frame);
        return NULL;
        }

SMBC_parse_path的代碼如下:

#define SMBC_PREFIX "smb:"

int SMBC_parse_path(TALLOC_CTX *ctx,
        SMBCCTX *context,
                const char *fname,
                char **pp_workgroup,
                char **pp_server,
                char **pp_share,
                char **pp_path,
        char **pp_user,
                char **pp_password,
                char **pp_options)
{
    char *s;
    const char *p;
    char *q, *r;
    char *workgroup = NULL;
    int len;

    /* Ensure these returns are at least valid pointers. */
    *pp_server = talloc_strdup(ctx, "");
    *pp_share = talloc_strdup(ctx, "");
    *pp_path = talloc_strdup(ctx, "");
    *pp_user = talloc_strdup(ctx, "");
    *pp_password = talloc_strdup(ctx, "");

    if (!*pp_server || !*pp_share || !*pp_path ||
            !*pp_user || !*pp_password) {
        return -1;
    }

        /*
         * Assume we wont find an authentication domain to parse, so default
         * to the workgroup in the provided context.
         */
    if (pp_workgroup != NULL) {
        *pp_workgroup =
                        talloc_strdup(ctx, smbc_getWorkgroup(context));
    }

    if (pp_options) {
        *pp_options = talloc_strdup(ctx, "");
    }
    s = talloc_strdup(ctx, fname);

    /* see if it has the right prefix */
    len = strlen(SMBC_PREFIX);
    if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
                return -1; /* What about no smb: ? */
        }

    p = s + len;

    /* Watch the test below, we are testing to see if we should exit */

    if (strncmp(p, "http://", 2) && strncmp(p, "\\\\", 2)) {
                DEBUG(1, ("Invalid path (does not begin with smb://"));
        return -1;
    }

    p += 2;  /* Skip the double slash */

        /* See if any options were specified */
        if ((q = strrchr(p, '?')) != NULL ) {
                /* There are options.  Null terminate here and point to them */
                *q++ = '\0';

                DEBUG(4, ("Found options '%s'", q));

        /* Copy the options */
        if (pp_options && *pp_options != NULL) {
            TALLOC_FREE(*pp_options);
            *pp_options = talloc_strdup(ctx, q);
        }
    }

    if (*p == '\0') {
        goto decoding;
    }

    if (*p == '/') {
        int wl = strlen(smbc_getWorkgroup(context));

        if (wl > 16) {
            wl = 16;
        }

        *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
        if (!*pp_server) {
            return -1;
        }
        (*pp_server)[wl] = '\0';
        return 0;
    }

    /*
     * ok, its for us. Now parse out the server, share etc.
     *
     * However, we want to parse out [[domain;]user[:password]@] if it
     * exists ...
     */

    /* check that '@' occurs before '/', if '/' exists at all */
    q = strchr_m(p, '@');
    r = strchr_m(p, '/');
    if (q && (!r || q < r)) {
        char *userinfo = NULL;
        const char *u;

        next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
        if (!userinfo) {
            return -1;
        }
        u = userinfo;

        if (strchr_m(u, ';')) {
            next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
            if (!workgroup) {
                return -1;
            }
            if (pp_workgroup) {
                *pp_workgroup = workgroup;
            }
        }

        if (strchr_m(u, ':')) {
            next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
            if (!*pp_user) {
                return -1;
            }
            *pp_password = talloc_strdup(ctx, u);
            if (!*pp_password) {
                return -1;
            }
        } else {
            *pp_user = talloc_strdup(ctx, u);
            if (!*pp_user) {
                return -1;
            }
        }
    }

    if (!next_token_talloc(ctx, &p, pp_server, "/")) {
        return -1;
    }

    if (*p == (char)0) {
        goto decoding;  /* That's it ... */
    }

    if (!next_token_talloc(ctx, &p, pp_share, "/")) {
        return -1;
    }

        /*
         * Prepend a leading slash if there's a file path, as required by
         * NetApp filers.
         */
        if (*p != '\0') {
        *pp_path = talloc_asprintf(ctx,
                                           "\\%s",
                                           p);
        } else {
        *pp_path = talloc_strdup(ctx, "");
    }
    if (!*pp_path) {
        return -1;
    }
    string_replace(*pp_path, '/', '\\');

decoding:
    (void) urldecode_talloc(ctx, pp_path, *pp_path);
    (void) urldecode_talloc(ctx, pp_server, *pp_server);
    (void) urldecode_talloc(ctx, pp_share, *pp_share);
    (void) urldecode_talloc(ctx, pp_user, *pp_user);
    (void) urldecode_talloc(ctx, pp_password, *pp_password);

    if (!workgroup) {
        workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
    }
    if (!workgroup) {
        return -1;
    }

    /* set the credentials to make DFS work */
    smbc_set_credentials_with_fallback(context,
                           workgroup,
                           *pp_user,
                           *pp_password);
    return 0;
}

粗略閱讀蠢笋,發(fā)現(xiàn)這個函數(shù)就是對url進行字符串處理的

其中包括url中的“@”和“/”等

(我當時很傻拨齐,通過此段代碼來反推smb鏈接的傳參規(guī)則)

我們直接看到上方的注釋(因為在定義與注釋之間有個宏吸引了我:#define SMBC_PREFIX "smb:")


/*
 * Function to parse a path and turn it into components
 *
 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
 * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
 * general format ("smb:" only; we do not look for "cifs:").
 *
 *
 * We accept:
 *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
 *
 * Meaning of URLs:
 *
 * smb://           Show all workgroups.
 *
 *                  The method of locating the list of workgroups varies
 *                  depending upon the setting of the context variable
 *                  context->options.browse_max_lmb_count.  This value
 *                  determines the maximum number of local master browsers to
 *                  query for the list of workgroups.  In order to ensure that
 *                  a complete list of workgroups is obtained, all master
 *                  browsers must be queried, but if there are many
 *                  workgroups, the time spent querying can begin to add up.
 *                  For small networks (not many workgroups), it is suggested
 *                  that this variable be set to 0, indicating query all local
 *                  master browsers.  When the network has many workgroups, a
 *                  reasonable setting for this variable might be around 3.
 *
 * smb://name/      if name<1D> or name<1B> exists, list servers in
 *                  workgroup, else, if name<20> exists, list all shares
 *                  for server ...
 *
 * If "options" are provided, this function returns the entire option list as a
 * string, for later parsing by the caller.  Note that currently, no options
 * are supported.
 */

#define SMBC_PREFIX "smb:"

在注釋中,我么終于找到了答案

/*
....
* We accept:
 *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
 ...
*/
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末昨寞,一起剝皮案震驚了整個濱河市瞻惋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌援岩,老刑警劉巖歼狼,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窄俏,居然都是意外死亡蹂匹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門凹蜈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來限寞,“玉大人,你說我怎么就攤上這事仰坦÷闹玻” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵悄晃,是天一觀的道長玫霎。 經(jīng)常有香客問我,道長妈橄,這世上最難降的妖魔是什么庶近? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮眷蚓,結果婚禮上鼻种,老公的妹妹穿的比我還像新娘。我一直安慰自己沙热,他們只是感情好叉钥,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布罢缸。 她就那樣靜靜地躺著,像睡著了一般投队。 火紅的嫁衣襯著肌膚如雪枫疆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天敷鸦,我揣著相機與錄音息楔,去河邊找鬼。 笑死轧膘,一個胖子當著我的面吹牛钞螟,可吹牛的內容都是我干的。 我是一名探鬼主播谎碍,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼洞焙!你這毒婦竟也來了蟆淀?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤澡匪,失蹤者是張志新(化名)和其女友劉穎熔任,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唁情,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡疑苔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了甸鸟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惦费。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抢韭,靈堂內的尸體忽然破棺而出薪贫,到底是詐尸還是另有隱情,我是刑警寧澤刻恭,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布瞧省,位于F島的核電站,受9級特大地震影響鳍贾,放射性物質發(fā)生泄漏鞍匾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一骑科、第九天 我趴在偏房一處隱蔽的房頂上張望橡淑。 院中可真熱鬧,春花似錦纵散、人聲如沸梳码。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掰茶。三九已至暇藏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間濒蒋,已是汗流浹背盐碱。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沪伙,地道東北人瓮顽。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像围橡,于是被迫代替她去往敵國和親暖混。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350