delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/2004/12/13/05:23:27

X-Authentication-Warning: delorie.com: mail set sender to djgpp-bounces using -f
From: <alexbodn AT 012 DOT net DOT il>
To: djgpp AT delorie DOT com
Subject: unc file names handling
Date: Mon, 13 Dec 2004 12:22:27 +0200
MIME-Version: 1.0
Message-Id: <20041213101818.LWSA10597.fep11@[212.117.129.234]>
Reply-To: djgpp AT delorie DOT com

This is a multi-part message in MIME format.

------=____1102933347759_-M7cY.O?DT
Content-Type: text/plain; charset=windows-1255
Content-Transfer-Encoding: 7bit


i have recently run into a failure to stat unc named files located on a network.

unc filename format: \\servername\directory_tree\filename.

this happens because the djgpp function _fixpath() is trying very hard to coerce every filename to the drive:\pathname\filename stanza, and is prepending the current drive to the pathname wherever slashes preceed it, as with unc.

changing this behaviour when dealing with unc filenames, allowed me to access information on those files, using stat().

the djdev source tree may be downloaded at ftp://ftp.delorie.com/pub/djgpp/current/v2/djlsr203.zip,
and src/libc/posix/sys/stat/fixpath.c should be applied the following patch:

--- d:/dosdev/djgpp/src/libc/posix/sys/stat/fixpath.c	2001-10-02 23:40:56.000000000 +0000
+++ ./fixpath.c	2004-12-13 11:00:00.000000000 +0000
@@ -136,6 +136,7 @@
   int		preserve_case = _preserve_fncase();
   char		*name_start;
   int		mbsize;
+  int       first;
 
   use_lfn = _use_lfn(in);
 
@@ -165,7 +166,7 @@
     }
     *op++ = *ip++;
   }
-  else
+  else if (*ip != &apos;\\&apos; || *(ip + 1) != &apos;\\&apos;) /*if not introducing unc*/
   {
     __dpmi_regs r;
     r.h.ah = 0x19;
@@ -180,14 +181,16 @@
     op = __get_current_directory(op, drive_number);
 
   /* Step through the input path */
+  first = 1;
   while (*ip)
   {
-    /* Skip input slashes */
-    if (is_slash(*ip))
+    /* Skip input slashes, if not introducing unc */
+    if (is_slash(*ip) && !(first && *(ip + 1) == &apos;\\&apos;))
     {
       ip++;
       continue;
     }
+    first = 0;
 
     /* Skip "." and output nothing */
     if (*ip == &apos;.&apos; && is_term(*(ip + 1)))

while it would be better to patch the library and make it again, you can overcome this problem by linking the patched fixpath.c (included with this message) with your project.

best regards,

alex


------=____1102933347759_-M7cY.O?DT
Content-Type: text/plain;
	name="Fixpath.c"
Content-Disposition: inline;
	filename="Fixpath.c"

/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <libc/stubs.h>
#include <stdio.h>		/* For FILENAME_MAX */
#include <stdlib.h>
#include <errno.h>		/* For errno */
#include <string.h>		/* For strlen() */
#include <fcntl.h>		/* For LFN stuff */
#include <go32.h>
#include <dpmi.h>		/* For dpmisim */
#include <crt0.h>		/* For crt0 flags */
#include <dos.h>		/* For Win NT version check */
#include <sys/stat.h>
#include <libc/dosio.h>

static unsigned use_lfn;

static char *__get_current_directory(char *out, int drive_number);

static char *
__get_current_directory(char *out, int drive_number)
{
  __dpmi_regs r;
  char tmpbuf[FILENAME_MAX];

  memset(&r, 0, sizeof(r));
  r.x.flags = 1;		/* Set carry for safety */
  if(use_lfn)
    r.x.ax = 0x7147;
  else
    r.h.ah = 0x47;
  r.h.dl = drive_number + 1;
  r.x.si = __tb_offset;
  r.x.ds = __tb_segment;
  __dpmi_int(0x21, &r);

  if (r.x.flags & 1)
  {
#ifdef TEST
    errno = __doserr_to_errno(r.x.ax);
    perror("Get dir failed in fixpath");
#endif
    *out++ = '.';	/* Return relative path (lfn=n on Win9x) */
    return out;
  }
  else
  {
    dosmemget(__tb, sizeof(tmpbuf), tmpbuf);
    strcpy(out+1,tmpbuf);

    if (*(out + 1) != '\0')
    {
      *out = '/';
      return out + strlen(out);
    } 
    else if (!use_lfn || _get_dos_version(1) != 0x532)
      /* Root path, don't insert "/", it'll be added later */
      return out;
  }

  /* Root path under WinNT/2K/XP with lfn (may be silent failure).
     If the short name equivalent of the current path is greater than
     64 characters, Windows 2000 and XP do not return the correct long
     path name - they return the root directory instead without any
     failure code.  Since this can be disastrous in deep directories
     doing an rm -rf, we check for this bug and try and fix the path. */

  r.x.ax = 0x7160;
  r.x.cx = 0x8002;	/* Get Long Path Name, using subst drive basis */
  r.x.es = __tb_segment;
  r.x.di = __tb_offset + FILENAME_MAX;
  
  tmpbuf[0] = drive_number + 'A';
  tmpbuf[1] = ':';
  tmpbuf[2] = '.';
  tmpbuf[3] = 0;
  _put_path(tmpbuf);

  __dpmi_int(0x21, &r);

  if (!(r.x.flags & 1))
  {
    dosmemget(__tb + FILENAME_MAX, sizeof(tmpbuf), tmpbuf);

    /* Validate return form and drive matches what _fixpath expects. */
    if (tmpbuf[0] == (drive_number + 'A') && tmpbuf[1] == ':')
    {
      strcpy(out, tmpbuf+2);	/* Trim drive, just directory */
      return out + strlen(out);
    }
  } 
#ifdef TEST
  else
  {
    errno = __doserr_to_errno(r.x.ax);
    perror("Truename failed in fixpath");
  }
#endif

  /* Fixpath failed or returned inconsistent info.  Return relative path. */
  *out++ = '.';
  return out;
}

__inline__ static int
is_slash(int c)
{
  return c == '/' || c == '\\';
}

__inline__ static int
is_term(int c)
{
  return c == '/' || c == '\\' || c == '\0';
}

/* Takes as input an arbitrary path.  Fixes up the path by:
   1. Removing consecutive slashes
   2. Removing trailing slashes
   3. Making the path absolute if it wasn't already
   4. Removing "." in the path
   5. Removing ".." entries in the path (and the directory above them)
   6. Adding a drive specification if one wasn't there
   7. Converting all slashes to '/'
 */
void
_fixpath(const char *in, char *out)
{
  int		drive_number;
  char		in1[FILENAME_MAX];
  char		*ip;
  char		*op = out;
  int		preserve_case = _preserve_fncase();
  char		*name_start;
  int		mbsize;
  int       first;

  use_lfn = _use_lfn(in);

  /* Perform the same magic conversions that _put_path does.  */
  _put_path(in);
  dosmemget(__tb, sizeof(in1), in1);
  ip = in1;

  /* Add drive specification to output string */
  if (((*ip >= 'a' && *ip <= 'z') ||
       (*ip >= 'A' && *ip <= 'Z'))
      && (*(ip + 1) == ':'))
  {
    if (*ip >= 'a' && *ip <= 'z')
    {
      drive_number = *ip - 'a';
      *op++ = *ip++;
    }
    else
    {
      drive_number = *ip - 'A';
      if (*ip <= 'Z')
	*op++ = drive_number + 'a';
      else
	*op++ = *ip;
      ++ip;
    }
    *op++ = *ip++;
  }
  else if (*ip != '\\' || *(ip + 1) != '\\') /*if not introducing unc*/
  {
    __dpmi_regs r;
    r.h.ah = 0x19;
    __dpmi_int(0x21, &r);
    drive_number = r.h.al;
    *op++ = drive_number + (drive_number < 26 ? 'a' : 'A');
    *op++ = ':';
  }

  /* Convert relative path to absolute */
  if (!is_slash(*ip))
    op = __get_current_directory(op, drive_number);

  /* Step through the input path */
  first = 1;
  while (*ip)
  {
    /* Skip input slashes, if not introducing unc */
    if (is_slash(*ip) && !(first && *(ip + 1) == '\\'))
    {
      ip++;
      continue;
    }
    first = 0;

    /* Skip "." and output nothing */
    if (*ip == '.' && is_term(*(ip + 1)))
    {
      ip++;
      continue;
    }

    /* Skip ".." and remove previous output directory */
    if (*ip == '.' && *(ip + 1) == '.' && is_term(*(ip + 2)))
    {
      ip += 2;
      if(out[2] == '.' && *(op - 1) == '.') 
      { 				/* relative path not skipped */
        *op++ = '/';
        *op++ = '.';
        *op++ = '.';
      } else
      /* Don't back up over drive spec */
      if (op > out + 2)
	/* This requires "/" to follow drive spec */
	while (!is_slash(*--op));
      continue;
    }

    /* Copy path component from in to out */
    *op++ = '/';
#if 0
    while (!is_term(*ip)) *op++ = *ip++;
#else
    while (!is_term(*ip))
      {
	mbsize = mblen (ip, MB_CUR_MAX);
	if (mbsize > 1)
	  {
	    /* copy multibyte character */
	    while (--mbsize >= 0)
	      *op++ = *ip++;
	  }
	else
	  *op++ = *ip++;
      }
#endif
  }

  /* If root directory, insert trailing slash */
  if (op == out + 2) *op++ = '/';

  /* Null terminate the output */
  *op = '\0';

  /* switch FOO\BAR to foo/bar, downcase where appropriate */
  for (op = out + 3, name_start = op - 1; *name_start; op++)
  {
    char long_name[FILENAME_MAX], short_name[13];

#if 1
    /* skip multibyte character */
    mbsize = mblen (op, MB_CUR_MAX);
    if (mbsize > 1)
      {
	op += mbsize - 1;
	continue;
      }
#endif
    if (*op == '\\')
      *op = '/';
    if (!preserve_case && (*op == '/' || *op == '\0'))
    {
      memcpy(long_name, name_start+1, op - name_start - 1);
      long_name[op - name_start - 1] = '\0';
      if (!strcmp(_lfn_gen_short_fname(long_name, short_name), long_name))
      {
#if 0
	while (++name_start < op)
	  if (*name_start >= 'A' && *name_start <= 'Z')
	    *name_start += 'a' - 'A';
#else
	while (++name_start < op)
	  {
	    mbsize = mblen (name_start, MB_CUR_MAX);
	    if (mbsize > 1)
	      {
		/* skip multibyte character */
		name_start += mbsize - 1;
		continue;
	      }
	    else if (*name_start >= 'A' && *name_start <= 'Z')
	      *name_start += 'a' - 'A';
	  }
#endif
      }
      else
	name_start = op;
    }
    else if (*op == '\0')
      break;
  }
}

#ifdef TEST

int main (int argc, char *argv[])
{
  char fixed[FILENAME_MAX];
  __dpmi_regs r;

  if (argc > 2) {
    _put_path(argv[1]);
    if(_USE_LFN)
      r.x.ax = 0x713b;
    else
      r.h.ah = 0x3b;
    r.x.dx = __tb_offset;
    r.x.ds = __tb_segment;
    __dpmi_int(0x21, &r);
    if(r.x.flags & 1) {
      errno = __doserr_to_errno(r.x.ax);
      sprintf(fixed, "Change dir to %s failed (lfn=%d)", argv[1], _USE_LFN);
      perror(fixed);
    } else
      printf("Set dir: %s\n", argv[1]);
    argc--;
    argv++;
  }

  if(_USE_LFN)
    r.x.ax = 0x7147;
  else
    r.h.ah = 0x47;
  r.h.dl = 0;
  r.x.si = __tb_offset;
  r.x.ds = __tb_segment;
  __dpmi_int(0x21, &r);
  if (r.x.flags & 1) {
    errno = __doserr_to_errno(r.x.ax);
    perror("getcwd failed");
  } else {
    dosmemget(__tb, sizeof(fixed), fixed);
    printf("Get dir[%d]: \\%s\n", strlen(fixed), fixed);
  }

  if (argc > 1)
    {
      _fixpath (argv[1], fixed);
      printf ("Fixpath: %s\n", fixed);
    }
  return 0;
}

#endif


------=____1102933347759_-M7cY.O?DT--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019