diff --git a/src/namedos.c b/src/namedos.c index 9cfcd72..993f49b 100644 --- a/src/namedos.c +++ b/src/namedos.c @@ -150,14 +150,32 @@ static void dos83_build_alias_candidate(const uint8 *src, int seq, snprintf((char *)out, out_size, "%s%s", stem, seqbuf); } -static int dos83_candidate_sequence(const uint8 *candidate) +static void dos83_build_plain_alias(const uint8 *src, uint8 *out, int out_size) { - const char *tilde = strchr((const char *)candidate, '~'); + char base[260], ext[260]; + char nbase[9], next[4]; - if (!tilde) - return 0; + dos83_split_name(src, base, sizeof(base), ext, sizeof(ext)); + dos83_normalize_part(base, nbase, sizeof(nbase), 8); + dos83_normalize_part(ext, next, sizeof(next), 3); - return atoi(tilde + 1); + if (!*nbase) + strcpy(nbase, "FILE"); + + if (*next) + snprintf((char *)out, out_size, "%s.%s", nbase, next); + else + snprintf((char *)out, out_size, "%s", nbase); +} + +static int dos83_same_plain_family(const uint8 *a, const uint8 *b) +{ + uint8 aa[DOS83_NAME_MAX + 1]; + uint8 bb[DOS83_NAME_MAX + 1]; + + dos83_build_plain_alias(a, aa, sizeof(aa)); + dos83_build_plain_alias(b, bb, sizeof(bb)); + return !strcmp((char *)aa, (char *)bb); } static int dos83_alias_used_in_dir(const char *dir_unix, @@ -167,7 +185,6 @@ static int dos83_alias_used_in_dir(const char *dir_unix, { DIR *d; struct dirent *dirbuff; - int candidate_seq = dos83_candidate_sequence(candidate); d = opendir(dir_unix); if (!d) { @@ -179,11 +196,10 @@ static int dos83_alias_used_in_dir(const char *dir_unix, return 0; XDPRINTF((2,0, - "DOS83 COLLISION CHECK dir='%s' src='%s' candidate='%s' seq=%d", + "DOS83 COLLISION CHECK dir='%s' src='%s' candidate='%s'", dir_unix ? dir_unix : "(null)", src ? (char *)src : "(null)", - candidate ? (char *)candidate : "(null)", - candidate_seq)); + candidate ? (char *)candidate : "(null)")); while ((dirbuff = readdir(d)) != NULL) { uint8 other[DOS83_NAME_MAX + 1]; @@ -195,51 +211,22 @@ static int dos83_alias_used_in_dir(const char *dir_unix, if (!strcmp(dirbuff->d_name, (const char *)src)) continue; - if (dos83_is_valid_name((uint8 *)dirbuff->d_name, options)) { - strncpy((char *)other, dirbuff->d_name, sizeof(other) - 1); - other[sizeof(other) - 1] = '\0'; - up_fn(other); - } else if (candidate_seq > 0) { - /* - * When the current candidate is a numbered alias, compare other long - * filenames against the same numbered candidate. This avoids treating - * every later long filename as if only ~1 mattered. - */ - dos83_build_alias_candidate((uint8 *)dirbuff->d_name, - candidate_seq, - other, - sizeof(other)); - } else { - char base[260], ext[260]; - char nbase[9], next[4]; + if (!dos83_is_valid_name((uint8 *)dirbuff->d_name, options)) + continue; - /* - * For the first unnumbered probe, compare other long filenames against - * the same unnumbered short alias. - */ - dos83_split_name((uint8 *)dirbuff->d_name, base, sizeof(base), ext, sizeof(ext)); - dos83_normalize_part(base, nbase, sizeof(nbase), 8); - dos83_normalize_part(ext, next, sizeof(next), 3); - - if (*nbase) { - if (*next) - snprintf((char *)other, sizeof(other), "%s.%s", nbase, next); - else - snprintf((char *)other, sizeof(other), "%s", nbase); - } else { - other[0] = '\0'; - } - } + strncpy((char *)other, dirbuff->d_name, sizeof(other) - 1); + other[sizeof(other) - 1] = '\0'; + up_fn(other); XDPRINTF((2,0, - "DOS83 COLLISION OTHER raw='%s' alias='%s' candidate='%s'", + "DOS83 COLLISION EXISTING8 raw='%s' alias='%s' candidate='%s'", dirbuff->d_name, other, candidate)); if (!strcmp((char *)other, (const char *)candidate)) { XDPRINTF((2,0, - "DOS83 COLLISION HIT raw='%s' alias='%s' candidate='%s'", + "DOS83 COLLISION HIT EXISTING8 raw='%s' alias='%s' candidate='%s'", dirbuff->d_name, other, candidate)); @@ -299,7 +286,46 @@ int dos83_build_name_in_dir(const char *dir_unix, } } - for (seq = 1; seq < 1000000; seq++) { + /* + * Number colliding long names by their directory order within the same + * unnumbered alias family. This gives stable ~1, ~2, ... aliases without + * comparing against hypothetical aliases of every other long filename. + */ + seq = 1; + if (dir_unix) { + DIR *d = opendir(dir_unix); + if (!d) { + if (seteuid(0)) {} + d = opendir(dir_unix); + reseteuid(); + } + + if (d) { + struct dirent *dirbuff; + while ((dirbuff = readdir(d)) != NULL) { + if (!dirbuff->d_ino) + continue; + if (!strcmp(dirbuff->d_name, ".") || !strcmp(dirbuff->d_name, "..")) + continue; + + if (!strcmp(dirbuff->d_name, (const char *)src)) + break; + + if (!dos83_is_valid_name((uint8 *)dirbuff->d_name, options) && + dos83_same_plain_family((uint8 *)dirbuff->d_name, src)) { + seq++; + XDPRINTF((2,0, + "DOS83 FAMILY PRIOR raw='%s' src='%s' seq_now=%d", + dirbuff->d_name, + src, + seq)); + } + } + closedir(d); + } + } + + for (; seq < 1000000; seq++) { dos83_build_alias_candidate(src, seq, out, out_size); if (!dir_unix || !dos83_alias_used_in_dir(dir_unix, src, out, options)) { XDPRINTF((2,0,