- if ( len == 0 ) {
- if ( !extend && movecwd( vol, dir ) < 0 ) {
- return( NULL );
- }
- return( path );
- }
-
- if ( *data == '\0' ) {
- data++;
- len--;
- }
-
- while ( *data == '\0' && len > 0 ) {
- if ( dir->d_parent == NULL ) {
- return( NULL );
- }
- dir = dir->d_parent;
- data++;
- len--;
- }
-
- /* would this be faster with strlen + strncpy? */
- p = path;
- while ( *data != '\0' && len > 0 ) {
- *p++ = *data++;
- len--;
- }
-
- /* short cut bits by chopping off a trailing \0. this also
- makes the traversal happy w/ filenames at the end of the
- cname. */
- if (len == 1)
- len--;
-
-#ifdef notdef
- /*
- * Dung Nguyen <ntd@adb.fr>
- *
- * AFPD cannot handle paths with "::" if the "::" notation is
- * not at the beginning of the path. The following path will not
- * be interpreted correctly:
- *
- * :a:b:::c: (directory c at the same level as directory a) */
- if ( len > 0 ) {
- data++;
- len--;
- }
-#endif /* notdef */
- *p = '\0';
-
- if ( p != path ) { /* we got something */
- if ( !extend ) {
- cdir = dir->d_child;
- while (cdir) {
- if ( strcasecmp( cdir->d_name, path ) == 0 ) {
- break;
- }
- cdir = (cdir == dir->d_child->d_prev) ? NULL :
- cdir->d_next;
- }
- if ( cdir == NULL ) {
- ++extend;
- if ( movecwd( vol, dir ) < 0 ) {
- return( NULL );
- }
- cdir = extenddir( vol, dir, path );
- }
-
- } else {
- cdir = extenddir( vol, dir, path );
- }
-
- if ( cdir == NULL ) {
- if ( len > 0 ) {
- return( NULL );
- }
-
- } else {
- dir = cdir;
- *path = '\0';
- }
- }
- }
+ if ( len == 0 ) {
+ if (movecwd( vol, dir ) < 0 ) {
+ return invalidate(vol, dir, &ret );
+ }
+ if (*path == '\0') {
+ ret.u_name = ".";
+ ret.d_dir = dir;
+ }
+ return &ret;
+ }
+
+ if (*data == sep ) {
+ data++;
+ len--;
+ }
+ while (*data == sep && len > 0 ) {
+ if ( dir->d_parent == NULL ) {
+ return NULL;
+ }
+ dir = dir->d_parent;
+ data++;
+ len--;
+ }
+
+ /* would this be faster with strlen + strncpy? */
+ p = path;
+ while ( *data != sep && len > 0 ) {
+ *p++ = *data++;
+ if (p > &path[ MAXPATHLEN]) {
+ afp_errno = AFPERR_PARAM;
+ return( NULL );
+ }
+ len--;
+ }
+
+ /* short cut bits by chopping off a trailing \0. this also
+ makes the traversal happy w/ filenames at the end of the
+ cname. */
+ if (len == 1)
+ len--;
+
+ *p = '\0';
+
+ if ( p == path ) { /* end of the name parameter */
+ continue;
+ }
+ ret.u_name = NULL;
+ if (afp_version >= 30) {
+ char *t;
+ cnid_t fileid;
+
+ if (toUTF8) {
+ static char temp[ MAXPATHLEN + 1];
+
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /*
+ With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
+ So we compare it with the longname from the current volume and if they match
+ we overwrite the requested path with the utf8 volume name so that the following
+ strcmp can match.
+ */
+ ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
+ if (strcasecmp( path, temp) == 0)
+ ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
+ } else {
+ /* toUTF8 */
+ if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
+ afp_errno = AFPERR_PARAM;
+ return( NULL );
+ }
+ strcpy(path, temp);
+ }
+ }
+ /* check for OS X mangled filename :( */
+
+ t = demangle_osx(vol, path, dir->d_did, &fileid);
+ if (t != path) {
+ ret.u_name = t;
+ /* duplicate work but we can't reuse all convert_char we did in demangle_osx
+ * flags weren't the same
+ */
+ if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
+ /* at last got our view of mac name */
+ strcpy(path,t);
+ }
+ }
+ }
+ if (ret.u_name == NULL) {
+ if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
+ afp_errno = AFPERR_PARAM;
+ return NULL;
+ }
+ }
+ if ( !extend ) {
+ ucs2_t *tmpname;
+ cdir = dir->d_child;
+ scdir = NULL;
+ if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
+ (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
+ CH_UCS2, path, strlen(path), (char **)&tmpname) )
+ {
+ while (cdir) {
+ if (!cdir->d_m_name_ucs2) {
+ LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
+ /* this shouldn't happen !!!! */
+ goto noucsfallback;
+ }
+
+ if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
+ break;
+ }
+ if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
+ scdir = cdir;
+ }
+ cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
+ }
+ free(tmpname);
+ }
+ else {
+noucsfallback:
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /*
+ root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
+ must check against the volume name.
+ */
+ if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
+ cdir = vol->v_dir;
+ else
+ cdir = NULL;
+ }
+ else {
+ cdir = dirsearch_byname(vol, dir, ret.u_name);
+ }
+ }
+
+ if (cdir == NULL && scdir != NULL) {
+ cdir = scdir;
+ /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
+ }
+
+ if ( cdir == NULL ) {
+ ++extend;
+ /* if dir == curdir it always succeed,
+ even if curdir is deleted.
+ it's not a pb because it will fail in extenddir
+ */
+ if ( movecwd( vol, dir ) < 0 ) {
+ /* dir is not valid anymore
+ we delete dir from the cache and abort.
+ */
+ if ( dir->d_did == DIRDID_ROOT_PARENT) {
+ afp_errno = AFPERR_NOOBJ;
+ return NULL;
+ }
+ if (afp_errno == AFPERR_ACCESS)
+ return NULL;
+ dir_invalidate(vol, dir);
+ return NULL;
+ }
+ cdir = extenddir( vol, dir, &ret );
+ }
+
+ } else {
+ cdir = extenddir( vol, dir, &ret );
+ } /* if (!extend) */
+
+ if ( cdir == NULL ) {
+
+ if ( len > 0 || !ret.u_name ) {
+ return NULL;
+ }
+
+ } else {
+ dir = cdir;
+ *path = '\0';
+ }
+ } /* for (;;) */