/* * namedos.c * * Centralized DOS 8.3 short-name support. */ #include "net.h" #include #include #include #include #include #include "nwfname.h" #include "nwvolume.h" #include "connect.h" #include "namedos.h" static int dos83_valid_char(int c) { if (c >= 'A' && c <= 'Z') return 1; if (c >= 'a' && c <= 'z') return 1; if (c >= '0' && c <= '9') return 1; if (c == '_' || c == '-') return 1; return 0; } static char dos83_upchar(int c) { if (c >= 'a' && c <= 'z') return (char)(c - 'a' + 'A'); return (char)c; } static void dos83_split_name(const uint8 *src, char *base, int base_sz, char *ext, int ext_sz) { const char *dot = strrchr((const char *)src, '.'); int len; if (base_sz > 0) base[0] = '\0'; if (ext_sz > 0) ext[0] = '\0'; if (dot && dot != (const char *)src) { len = (int)(dot - (const char *)src); if (len >= base_sz) len = base_sz - 1; memcpy(base, src, len); base[len] = '\0'; strncpy(ext, dot + 1, ext_sz - 1); ext[ext_sz - 1] = '\0'; } else { strncpy(base, (const char *)src, base_sz - 1); base[base_sz - 1] = '\0'; } } static void dos83_normalize_part(const char *src, char *dst, int dst_sz, int maxlen) { int di = 0; while (*src && di < dst_sz - 1 && di < maxlen) { unsigned char c = (unsigned char)*src++; if (c == ' ') continue; if (dos83_valid_char(c)) dst[di++] = dos83_upchar(c); } dst[di] = '\0'; } static void dos83_normalize_part_full(const char *src, char *dst, int dst_sz) { int di = 0; while (*src && di < dst_sz - 1) { unsigned char c = (unsigned char)*src++; if (c == ' ') continue; if (dos83_valid_char(c)) dst[di++] = dos83_upchar(c); } dst[di] = '\0'; } int dos83_is_valid_name(const uint8 *name, int options) { const uint8 *ss = name; int len = 0; int pf = 0; for (; *ss; ss++) { if (*ss == '.') { if (pf++) return 0; len = 0; } else { ++len; if ((pf && len > 3) || len > 8) return 0; if (!(options & VOL_OPTION_IGNCASE)) { if (options & VOL_OPTION_DOWNSHIFT) { if (*ss >= 'A' && *ss <= 'Z') return 0; } else { if (*ss >= 'a' && *ss <= 'z') return 0; } } if (!dos83_valid_char(*ss)) return 0; } } return (*name != '\0'); } static void dos83_build_alias_candidate(const uint8 *src, int seq, uint8 *out, int out_size) { char base[260], ext[260]; char nbase_full[260], next[4]; char stem[9]; char seqbuf[16]; int stem_len; dos83_split_name(src, base, sizeof(base), ext, sizeof(ext)); dos83_normalize_part_full(base, nbase_full, sizeof(nbase_full)); dos83_normalize_part(ext, next, sizeof(next), 3); if (!*nbase_full) strcpy(nbase_full, "FILE"); sprintf(seqbuf, "~%d", seq); stem_len = 8 - (int)strlen(seqbuf); if (stem_len > 6) stem_len = 6; if (stem_len < 1) stem_len = 1; if (stem_len > (int)strlen(nbase_full)) stem_len = (int)strlen(nbase_full); memcpy(stem, nbase_full, stem_len); stem[stem_len] = '\0'; if (*next) snprintf((char *)out, out_size, "%s%s.%s", stem, seqbuf, next); else snprintf((char *)out, out_size, "%s%s", stem, seqbuf); } static void dos83_build_plain_alias(const uint8 *src, uint8 *out, int out_size) { char base[260], ext[260]; char nbase[9], next[4]; 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); 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_compute_family_rank(const char *dir_unix, const uint8 *src, int options) { DIR *d; struct dirent *dirbuff; int rank = 0; if (!dir_unix) return 0; d = opendir(dir_unix); if (!d) { if (seteuid(0)) {} d = opendir(dir_unix); reseteuid(); } if (!d) return 0; 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)) rank++; } closedir(d); return rank; } static void dos83_build_ranked_alias(const uint8 *src, int rank, uint8 *out, int out_size) { if (rank <= 0) dos83_build_plain_alias(src, out, out_size); else dos83_build_alias_candidate(src, rank, out, out_size); } static int dos83_alias_used_in_dir(const char *dir_unix, const uint8 *src, const uint8 *candidate, int options) { DIR *d; struct dirent *dirbuff; int src_rank = dos83_compute_family_rank(dir_unix, src, options); d = opendir(dir_unix); if (!d) { if (seteuid(0)) {} d = opendir(dir_unix); reseteuid(); } if (!d) return 0; XDPRINTF((2,0, "DOS83 COLLISION CHECK dir='%s' src='%s' candidate='%s' src_rank=%d", dir_unix ? dir_unix : "(null)", src ? (char *)src : "(null)", candidate ? (char *)candidate : "(null)", src_rank)); while ((dirbuff = readdir(d)) != NULL) { uint8 other[DOS83_NAME_MAX + 1]; if (!dirbuff->d_ino) continue; if (!strcmp(dirbuff->d_name, ".") || !strcmp(dirbuff->d_name, "..")) continue; 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); XDPRINTF((2,0, "DOS83 COLLISION EXISTING8 raw='%s' alias='%s' candidate='%s'", dirbuff->d_name, other, candidate)); if (!strcmp((char *)other, (const char *)candidate)) { closedir(d); return 1; } continue; } if (dos83_same_plain_family((uint8 *)dirbuff->d_name, src)) { int other_rank = dos83_compute_family_rank(dir_unix, (uint8 *)dirbuff->d_name, options); dos83_build_ranked_alias((uint8 *)dirbuff->d_name, other_rank, other, sizeof(other)); XDPRINTF((2,0, "DOS83 COLLISION FAMILY raw='%s' alias='%s' candidate='%s' other_rank=%d", dirbuff->d_name, other, candidate, other_rank)); if (!strcmp((char *)other, (const char *)candidate)) { closedir(d); return 1; } } } closedir(d); return 0; } int dos83_build_name_in_dir(const char *dir_unix, const uint8 *src, uint8 *out, int out_size, int options) { int rank; if (!src || !*src || !out || out_size <= 0) return 0; if (dos83_is_valid_name(src, options)) { strncpy((char *)out, (const char *)src, out_size - 1); out[out_size - 1] = '\0'; up_fn(out); XDPRINTF((2,0, "DOS83 BUILD dir='%s' src='%s' out='%s' valid=%d", dir_unix ? dir_unix : "(null)", src ? (char *)src : "(null)", out ? (char *)out : "(null)", dos83_is_valid_name(src, options))); return (int)strlen((char *)out); } rank = dos83_compute_family_rank(dir_unix, src, options); dos83_build_ranked_alias(src, rank, out, out_size); XDPRINTF((2,0, "DOS83 BUILD dir='%s' src='%s' out='%s' valid=%d rank=%d", dir_unix ? dir_unix : "(null)", src ? (char *)src : "(null)", out ? (char *)out : "(null)", dos83_is_valid_name(src, options), rank)); if (dir_unix && dos83_alias_used_in_dir(dir_unix, src, out, options)) { int seq = rank > 0 ? rank : 1; do { seq++; dos83_build_alias_candidate(src, seq, out, out_size); XDPRINTF((2,0, "DOS83 BUILD retry dir='%s' src='%s' out='%s' seq=%d", dir_unix ? dir_unix : "(null)", src ? (char *)src : "(null)", out ? (char *)out : "(null)", seq)); } while (seq < 1000000 && dos83_alias_used_in_dir(dir_unix, src, out, options)); } return (int)strlen((char *)out); } static int dos83_strip_magic(const uint8 *in, uint8 *out, int out_size) { const uint8 *p = in; uint8 *q = out; while (*p && (q - out) < out_size - 1) { if (*p != 0xff) *q++ = *p; p++; } *q = '\0'; return (int)(q - out); } static int dos83_x_str_match(const uint8 *s, const uint8 *p, int soptions) { uint8 pbuf[256]; dos83_strip_magic(p, pbuf, sizeof(pbuf)); return fn_dos_match((uint8 *)s, pbuf, soptions); } int dos83_match_name_in_dir(const char *dir_unix, const uint8 *unix_name, const uint8 *dos_pattern, int options, uint8 *out_dos_name, int out_dos_name_size) { uint8 dos_name[DOS83_NAME_MAX + 1]; if (!unix_name || !dos_pattern) return 0; dos83_build_name_in_dir(dir_unix, unix_name, dos_name, sizeof(dos_name), options); if (out_dos_name && out_dos_name_size > 0) { strncpy((char *)out_dos_name, (const char *)dos_name, out_dos_name_size - 1); out_dos_name[out_dos_name_size - 1] = '\0'; } { int match_result = dos83_x_str_match(dos_name, dos_pattern, options); XDPRINTF((2,0, "DOS83 MATCH dir='%s' unix='%s' pattern='%s' alias='%s' result=%d", dir_unix ? dir_unix : "(null)", unix_name ? (char *)unix_name : "(null)", dos_pattern ? (char *)dos_pattern : "(null)", dos_name, match_result)); return match_result; } } int dos83_resolve_component(const char *dir_unix, const uint8 *dos_name, uint8 *resolved_name, int resolved_size, int options) { DIR *d; struct dirent *dirbuff; uint8 clean_name[256]; if (!dir_unix || !dos_name || !*dos_name || !resolved_name || resolved_size <= 0) return 0; dos83_strip_magic(dos_name, clean_name, sizeof(clean_name)); up_fn(clean_name); d = opendir(dir_unix); if (!d) { if (seteuid(0)) {} d = opendir(dir_unix); reseteuid(); } if (!d) return 0; while ((dirbuff = readdir(d)) != NULL) { uint8 dos_alias[DOS83_NAME_MAX + 1]; if (!dirbuff->d_ino) continue; dos83_build_name_in_dir(dir_unix, (uint8 *)dirbuff->d_name, dos_alias, sizeof(dos_alias), options); if (!strcmp((char *)dos_alias, (const char *)clean_name)) { strncpy((char *)resolved_name, dirbuff->d_name, resolved_size - 1); resolved_name[resolved_size - 1] = '\0'; closedir(d); return 1; } } closedir(d); return 0; } static int dos83_get_match(uint8 *unixname, uint8 *p, int options) { DIR *d; if (!p || !*p) return 1; *p = '\0'; d = opendir((char *)unixname); if (!d) { if (seteuid(0)) {} d = opendir((char *)unixname); reseteuid(); } if (d) { struct dirent *dirbuff; *p = '/'; while ((dirbuff = readdir(d)) != NULL) { int len; uint8 resolved[256]; if (!dirbuff->d_ino) continue; if (dos83_resolve_component((char *)unixname, p + 1, resolved, sizeof(resolved), options)) { strncpy((char *)(p + 1), (const char *)resolved, 255); closedir(d); len = (int)strlen((char *)(p + 1)); return dos83_get_match(unixname, p + 1 + len, options); } } closedir(d); } else { *p = '/'; } return 0; } void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp) { struct stat stbuff; if (!stat((char *)unixname, &stbuff)) return; dos83_get_match(unixname, pp - 1, vol->options); }