/* Unix SMB/Netbios implementation. Version 1.9. Copyright (C) Andrew Tridgell 1992,1993,1994 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Modified by O.V.Zhirov, July 1998 */ #ifdef DOSEMU #include "emu.h" #include "mangle.h" #include "mfs.h" #include "dos2linux.h" #include "utilities.h" #include "translate/translate.h" #include #include #ifdef HAVE_LIBBSD #include #endif #else #include "includes.h" #include "loadparm.h" #endif static void mangle_name_83(char *s, char *MangledMap); /**************************************************************************** provide a checksum on a string ****************************************************************************/ static int str_checksum(char *s) { int res = 0; int c; int i=0; while (*s) { c = *s; res ^= ((unsigned)c << (i % 15)) ^ ((unsigned)c >> (15-(i%15))); s++; i++; } return(res); } /**************************************************************************** check if a name is a special msdos reserved name: the name is either a full Unix name or an 8 character candidate ****************************************************************************/ FAR_PTR is_dos_device(const char *path) { char *p; const char *fname; dosaddr_t dev; far_t devfar; int i; int cnt; /* C:\DEV notation is allowed */ fname = strrchr(path, '\\'); if (fname) fname++; else fname = path; /* * LPTx e.t.c. is reserved no matter the path (e.g. .\LPT1 _is_ reserved), * but mfs.c sends an entire path. * -- Rob Clark 2000/05/09 */ if (fname[0] == '/') { d_printf("MFS: is_msdos_device %s\n", fname); p = strrchr(fname,'/'); if (p) fname = p+1; /* strip the directory part */ } else { d_printf("MFS: is_msdos_device %.8s\n", fname); } /* * The below (now #ifdef'ed out) code strips the extension off for checking. * But the assumption, that file names with a reserved basename remain * reserved even if they have an extension, seems not to be true. * For example CLIPPER-programs _are_ using file names such as * 'LPTx.PRN' for internal spooling purposes. * -- Bernd Schueler b.schueler@gmx.de 2001/02/23 * well, as far as I know the extension can be stripped off; * most likely the bug was somewhere else - we must not strip * the extension off for further processing Bart 2002/08/12 */ /* walk the chain of DOS devices; see also FreeDOS kernel code */ devfar = get_nuldev(); dev = FAR2ADDR(devfar); cnt = 0; do { for (i = 0; i < 8; i++) { char c1 = fname[i]; if (c1 == '.' || c1 == '\0') { /* check if remainder of device name consists of spaces or nulls */ for (; i < 8; i++) { char c2 = READ_BYTE(dev + 0xa + i); if (c2 != ' ' && c2 != '\0') break; } break; } if (toupperDOS(c1) != toupperDOS(READ_BYTE(dev + 0xa + i))) break; } if (i == 8) return MK_FP(devfar); if (READ_BYTE(dev) == 0xff || READ_BYTE(dev+1) == 0xff) return 0; devfar = rFAR_FARt(READ_DWORD(dev)); dev = FAR2ADDR(devfar); } while (cnt++ < 256); error("MFS: DOS device list corrupted\n"); leavedos(17); return 0; } /**************************************************************************** return True if a name is in 8.3 dos format ****************************************************************************/ static BOOL is_8_3(char *fname) { int len = strlen(fname); char *dot_pos = strchr(fname,'.'); int l; DEBUG(5,("checking %s for 8.3\n",fname)); /* can't be longer than 12 chars */ if (len == 0 || len > 12) return(False); /* can't contain invalid dos chars */ /* Windows use the ANSI charset. But filenames are translated in the PC charset. This Translation may be more or less relaxed depending the Windows application. */ /* %%% A nice improvment to name mangling would be to translate filename to ANSI charset on the smb server host */ { char *p = fname; #ifdef KANJI dot_pos = 0; while (*p) { if (is_shift_jis (*p)) { p += 2; } else if (is_kana (*p)) { p ++; } else { if (*p == '.' && !dot_pos) dot_pos = (char *) p; if (!VALID_DOS_PCHAR(p)) return(False); p++; } } #else while (*p) { if (!VALID_DOS_PCHAR(p)) return(False); p++; } #endif /* KANJI */ } /* no dot and less than 9 means OK */ if (!dot_pos) return(len <= 8); l = PTR_DIFF(dot_pos,fname); /* base must be at least 1 char except special cases . and .. */ if (l == 0) return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0); /* base can't be greater than 8 */ if (l > 8) return(False); if (lp_strip_dot() && len - l == 1 && !strchr(dot_pos+1,'.')) { *dot_pos = 0; return(True); } /* extension must be between 1 and 3 */ if ( (len - l < 2 ) || (len - l > 4) ) return(False); /* extension can't have a dot */ if (strchr(dot_pos+1,'.')) return(False); /* must be in 8.3 format */ return(True); } /* keep a stack of name mangling results - just so file moves and copies have a chance of working */ fstring *mangled_stack = NULL; int mangled_stack_size = 0; int mangled_stack_len = 0; /**************************************************************************** create the mangled stack ****************************************************************************/ static void create_mangled_stack(int size) { if (mangled_stack) { free(mangled_stack); mangled_stack_size = 0; mangled_stack_len = 0; } if (size > 0) mangled_stack = (fstring *)malloc(sizeof(fstring)*size); if (mangled_stack) mangled_stack_size = size; } /**************************************************************************** push a mangled name onto the stack ****************************************************************************/ static void push_mangled_name(char *s) { int i; char *p; if (!mangled_stack) { create_mangled_stack(MANGLED_STACK); if (!mangled_stack) return; } for (i=0;i 0xffff) symbol = '_'; *dest = unicode_to_dos_table(symbol); if (!VALID_DOS_PCHAR(dest) && strchr(" +,;=[]",*dest)==0) retval = 0; dest++; } *dest = '\0'; return retval; } /**************************************************************************** convert a filename to 8.3 format. return True if successful. ****************************************************************************/ BOOL name_convert(char *Name, BOOL mangle) { /* check if it's already in 8.3 format */ if (is_8_3(Name)) return(True); if (!mangle) return(False); DEBUG(5,("Converted name %s",Name)); /* mangle it into 8.3 */ push_mangled_name(Name); mangle_name_83(Name, NULL); DEBUG(5,("to %s\n",Name)); return(True); } #ifndef DOSEMU static char *mangled_match(char *s, /* This is null terminated */ char *pattern, /* This isn't. */ int len) /* This is the length of pattern. */ { static pstring matching_bit; /* The bit of the string which matches */ /* a * in pattern if indeed there is a * */ char *sp; /* Pointer into s. */ char *pp; /* Pointer into p. */ char *match_start; /* Where the matching bit starts. */ pstring pat; StrnCpy(pat, pattern, len); /* Get pattern into a proper string! */ strcpy(matching_bit,""); /* Match but no star gets this. */ pp = pat; /* Initialise the pointers. */ sp = s; if ((len == 1) && (*pattern == '*')) { return NULL; /* Impossible, too ambiguous for */ /* words! */ } while ((*sp) /* Not the end of the string. */ && (*pp) /* Not the end of the pattern. */ && (*sp == *pp) /* The two match. */ && (*pp != '*')) { /* No wildcard. */ sp++; /* Keep looking. */ pp++; } if (!*sp && !*pp) /* End of pattern. */ return matching_bit; /* Simple match. Return empty string. */ if (*pp == '*') { pp++; /* Always interrested in the chacter */ /* after the '*' */ if (!*pp) { /* It is at the end of the pattern. */ StrnCpy(matching_bit, s, sp-s); return matching_bit; } else { /* The next character in pattern must match a character further */ /* along s than sp so look for that character. */ match_start = sp; while ((*sp) /* Not the end of s. */ && (*sp != *pp)) /* Not the same */ sp++; /* Keep looking. */ if (!*sp) { /* Got to the end without a match. */ return NULL; } else { /* Still hope for a match. */ /* Now sp should point to a matching character. */ StrnCpy(matching_bit, match_start, sp-match_start); /* Back to needing a stright match again. */ while ((*sp) /* Not the end of the string. */ && (*pp) /* Not the end of the pattern. */ && (*sp == *pp)) { /* The two match. */ sp++; /* Keep looking. */ pp++; } if (!*sp && !*pp) /* Both at end so it matched */ return matching_bit; else return NULL; } } } return NULL; /* No match. */ } BOOL do_fwd_mangled_map(char *s, char *MangledMap) { /* MangledMap is a series of name pairs in () separated by spaces. * If s matches the first of the pair then the name given is the * second of the pair. A * means any number of any character and if * present in the second of the pair as well as the first the * matching part of the first string takes the place of the * in the * second. * * I wanted this so that we could have RCS files which can be used * by UNIX and DOS programs. My mapping string is (RCS rcs) which * converts the UNIX RCS file subdirectory to lowercase thus * preventing mangling. */ char *start=MangledMap; /* Use this to search for mappings. */ char *end; /* Used to find the end of strings. */ char *match_string; pstring new_string; /* Make up the result here. */ char *np; /* Points into new_string. */ DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap)); while (*start) { while ((*start) && (*start != '(')) start++; start++; /* Skip the ( */ if (!*start) continue; /* Always check for the end. */ end = start; /* Search for the ' ' or a ')' */ DEBUG(5,("Start of first in pair '%s'\n", start)); while ((*end) && !((*end == ' ') || (*end == ')'))) end++; if (!*end) { start = end; continue; /* Always check for the end. */ } DEBUG(5,("End of first in pair '%s'\n", end)); if ((match_string = mangled_match(s, start, end-start))) { DEBUG(5,("Found a match\n")); /* Found a match. */ start = end+1; /* Point to start of what it is to become. */ DEBUG(5,("Start of second in pair '%s'\n", start)); end = start; np = new_string; while ((*end) /* Not the end of string. */ && (*end != ')') /* Not the end of the pattern. */ && (*end != '*')) /* Not a wildcard. */ *np++ = *end++; if (!*end) { start = end; continue; /* Always check for the end. */ } if (*end == '*') { strcpy(np, match_string); np += strlen(match_string); end++; /* Skip the '*' */ while ((*end) /* Not the end of string. */ && (*end != ')') /* Not the end of the pattern. */ && (*end != '*')) /* Not a wildcard. */ *np++ = *end++; } if (!*end) { start = end; continue; /* Always check for the end. */ } *np++ = '\0'; /* NULL terminate it. */ DEBUG(5,("End of second in pair '%s'\n", end)); strcpy(s, new_string); /* Substitute with the new name. */ strupperDOS(s); DEBUG(5,("s is now '%s'\n", s)); return True; /* DONE! */ } else { start = end; /* Skip a bit which cannot be wanted */ /* anymore. */ } start++; } return False; } /******************************************************/ static BOOL do_rev_mangled_map(char *s, char *MangledMap) { /* Does the reverse of do_fwd_mangled_map(). Look in there for * details. */ /* Look for the start and end of each of the mapping strings using * these... */ char *start_first=MangledMap; char *end_first; char *start_second=MangledMap; char *end_second; char *match_string; char *ss; /* Pointer into s. */ char *pp; /* Pointer into pattern. */ pstring pattern; DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap)); while (*start_first) { while ((*start_first) && (*start_first != '(')) start_first++; start_first++; /* Skip the ( */ if (!*start_first) continue; /* Always check for the end. */ end_first = start_first; /* Search for the ' ' or a ')' */ DEBUG(5,("Start of first in pair '%s'\n", start_first)); while ((*end_first) && !((*end_first == ' ') || (*end_first == ')'))) end_first++; if (!*end_first) { start_first = end_first; continue; /* Always check for the end. */ } DEBUG(5,("End of first in pair '%s'\n", end_first)); /* Now look for the second pair. */ start_second = end_first+1; /* Point to start of second string in */ /* pair. */ DEBUG(5,("Start of second in pair '%s'\n", start_second)); end_second = start_second; /* Find the end of the second of the pair. */ while ((*end_second) && (*end_second != ')')) end_second++; if (!*end_second) { start_first = end_second; continue; /* Always check for the end. */ } DEBUG(5,("End of second in pair '%s'\n", end_second)); if ((match_string=mangled_match(s, start_second, end_second-start_second))) { DEBUG(5,("Found a match\n")); /* Substitute with the new name. */ ss = s; StrnCpy(pattern, start_first, end_first-start_first); pp = pattern; while ((*pp) /* Not end of pattern. */ && (*pp != '*')) /* Not the wild bit. */ *ss++ = *pp++; if (!*pp) { /* The end of the pattern. */ DEBUG(5,("s is now '%s'\n", s)); return True; } /* Must have hit a star. */ pp++; /* Skip the star. */ strcat(ss, match_string); /* Bung in the matching bit. */ ss += strlen(match_string); while ((*ss++ = *pp++)) /* Not end of pattern. */ ; DEBUG(5,("s is now '%s'\n", s)); return True; /* DONE! */ } else { start_first = end_second; /* Skip a bit which cannot be wanted */ /* anymore. */ } start_first++; } return False; } /**************************************************************************** search for a name in a directory which when 83 mangled gives the specified string ****************************************************************************/ void mangle_search_83(char *s,char *home,char *dir, char *MangledMap) { void *dirptr; char *dname; pstring tmpname; DEBUG(3,("mangle search - searching for %s in %s\n",s,dir)); if (check_mangled_stack(s, MangledMap)) return; strcpy(tmpname,home); if (home[strlen(home)-1] != '/' && *dir != '/') strcat(tmpname,"/"); strcat(tmpname,dir); if (tmpname[strlen(tmpname)-1] == '/') strcat(tmpname,"."); dirptr = OpenDir(tmpname); if (!dirptr) return; for (dname = ReadDirName(dirptr); dname; dname = ReadDirName(dirptr)) { strcpy(tmpname,dname); mangle_name_83(tmpname, MangledMap); DEBUG(5,("Mangle: Trying %s gave %s\n",dname,tmpname)); if (strcmp(tmpname,s) == 0) { strcpy(s,dname); push_mangled_name(s); CloseDir(dirptr); return; } } /* didn't find it */ strnorm(s); CloseDir(dirptr); } /**************************************************************************** convert a dos name to a unix name - possibly unmangling ****************************************************************************/ void unix_convert_83(char *s,char *home,BOOL mangle, char *MangledMap) { char *p; unix_format(s); strnorm(s); if (!mangle) return; if (MangledMap && *MangledMap) { /* I now have to scan the components of this file path to see if */ /* they need to be reversed mapped. */ char *d; /* Destination string pointer. */ char *start; pstring component; *component = 0; p = s; /* Use p to scan the string. */ while (*p) { if ((*p == '/') || (p == s)) { /* Path separator or start of string. */ if (*p == '/') p++; /* get to the name bit. */ start = p; /* And remember its start position. */ d = component; while (*p && (*p != '/')) /* And copy it into component. */ *d++ = *p++; *d++ = '\0'; if (do_rev_mangled_map(component, MangledMap)) { pstring new_s; StrnCpy(new_s, s, start-s); strcat(new_s, component); strcat(new_s, p); strcpy(s, new_s); p = start + strlen(component); } } else { p++; /* I shouldn't get here. */ } } } DEBUG(5,("Converting name %s ",s)); unix_clean_name(s); /* Now need to do a reverse lookup in MangledMap as I will have got * the second of the pair from the client and I want to map to the * first. */ while ((p = strchr(s,magic_char))) { char *p2; pstring directory; pstring name; pstring rest; *directory = *name = *rest = 0; /* pull off the rest of the name */ if ((p2 = strchr(p,'/'))) { *p2 = 0; strcpy(rest,p2+1); } /* pull out the name itself */ if ((p2 = strrchr(s,'/'))) { *p2 = 0; strcpy(name,p2+1); strcpy(directory,s); } else strcpy(name,s); strupperDOS(name); /* now search through the directory for a matching name */ mangle_search_83(name,home,directory, MangledMap); string_replace(name,magic_char,1); /* and construct the name again */ strcpy(s,directory); if (*directory) strcat(s,"/"); strcat(s,name); if (*rest) strcat(s,"/"); strcat(s,rest); } string_replace(s,1,magic_char); DEBUG(5,("to %s\n",s)); } #endif