bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] ftpfs/libftpconn


From: Moritz Schulte
Subject: [PATCH] ftpfs/libftpconn
Date: Sat, 13 Apr 2002 02:25:07 +0200
User-agent: Gnus/5.090004 (Oort Gnus v0.04) Emacs/21.2

Hello,

I investigated the problem, which caused ftpfs to fail with some ftp
servers (e.g. ftp.debian.org).  The problem is that libftpconn
sometimes tries to 'list' _directories_ and not _entries in a
directory_.  But obviously that isn't supported by all ftp servers,
see:

lftp ftp.debian.org:~> ls -d /
drwxr-xr-x    8 1176     1176         4096 Apr 12 21:27 debian
drwxrwsr-x    3 1176     1176         4096 Nov 22 09:39 debian-archive
drwxr-xr-x    2 0        0           16384 Dec 14 23:01 lost+found
lftp ftp.debian.org:/>

lftp ftp.de.debian.org:~> ls -d /
drwxr-xr-x   8 ftp      ftp          4096 Feb 17 18:49 /
lftp ftp.de.debian.org:/>

I haven't found a way in the FTP protocol, which helps us here.  The
obvious workaround for the case in which /foo/bar should be listed is
is to list '/foo' and search for 'bar'.

That is what my patch for libftpconn does.  The patch for ftpfs does
two things: 0) disable 'list's of the root node (that doesn't make
much sense anyway); 1) initialize the root node's stat information.

I hope everything is alright.

ftpfs:

2002-04-13  Moritz Schulte  <moritz@chaosdorf.de>

        * ftpfs.c: Include <sys/stat.h>
        (main): Stat the underlying node and initialize the root node's
        stat information.

        * dir.c (ftpfs_refresh_node): If refreshing the root node, simply
        use the old stat information.

libftpconn:

2002-04-13  Moritz Schulte  <moritz@chaosdorf.de>

        * unix.c: Include <libgen.h>.
        (struct get_stats_state): New member: searched_name.
        (ftp_conn_unix_start_get_stats): Return EINVAL if trying to list
        the root node without listing it's content; set searched_name to
        the dirname.
        (ftp_conn_unix_cont_get_stats): If searching for the list info of
        one entry, skip all other entries.

diff -rup ftpfs.orig/dir.c ftpfs/dir.c
--- ftpfs.orig/dir.c    Sun Jan  7 20:31:49 2001
+++ ftpfs/dir.c Sat Apr 13 01:42:35 2002
@@ -483,8 +483,10 @@ ftpfs_refresh_node (struct node *node)
              if (!err && entry->noent)
                err = ENOENT;
            }
-         else
+         else if (*(entry->name))
            {
+             /* Not for the root node.  */
+
              struct ftp_conn *conn;
 
              err = ftpfs_get_ftp_conn (dir->fs, &conn);
@@ -517,6 +519,17 @@ ftpfs_refresh_node (struct node *node)
                  entry->noent = 1; /* A negative entry.  */
                  entry->name_timestamp = timestamp;
                }
+           }
+         else
+           {
+             /* Now the root node.  Simply refresh the node with the
+                 old stat information.  */
+             struct refresh_entry_state res;
+             res.entry = entry;
+             res.timestamp = timestamp;
+             err = update_old_entry (entry->name,
+                                     &netfs_root_node->nn_stat,
+                                     NULL, &res);
            }
        }
 
diff -rup ftpfs.orig/ftpfs.c ftpfs/ftpfs.c
--- ftpfs.orig/ftpfs.c  Tue Mar 12 04:30:19 2002
+++ ftpfs/ftpfs.c       Sat Apr 13 01:43:21 2002
@@ -24,6 +24,7 @@
 #include <error.h>
 #include <argz.h>
 #include <netdb.h>
+#include <sys/stat.h>
 
 #include <version.h>
 
@@ -365,7 +366,8 @@ int
 main (int argc, char **argv)
 {
   error_t err;
-  mach_port_t bootstrap;
+  mach_port_t bootstrap, underlying_node;
+  struct stat underlying_stat;
   const struct argp_child argp_children[] =
     { {&common_argp}, {&netfs_std_startup_argp}, {0} };
   struct argp argp =
@@ -395,7 +397,27 @@ main (int argc, char **argv)
 
   netfs_root_node = ftpfs->root;
 
-  netfs_startup (bootstrap, 0);
+  underlying_node = netfs_startup (bootstrap, 0);
+  err = io_stat (underlying_node, &underlying_stat);
+  if (err)
+    error (1, err, "cannot stat underling node");
+
+  /* Initialize stat information of the root node.  */
+  netfs_root_node->nn_stat = underlying_stat;
+  netfs_root_node->nn_stat.st_mode =
+    S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+
+  /* If the underlying node isn't a directory, propagate read permission to
+     execute permission since we need that for lookups.  */
+  if (! S_ISDIR (underlying_stat.st_mode))
+    {
+      if (underlying_stat.st_mode & S_IRUSR)
+       netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+      if (underlying_stat.st_mode & S_IRGRP)
+       netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+      if (underlying_stat.st_mode & S_IROTH)
+       netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+    }
 
   for (;;)
     netfs_server_loop ();

diff -rup libftpconn.orig/unix.c libftpconn/unix.c
--- libftpconn.orig/unix.c      Tue Oct 20 10:33:37 1998
+++ libftpconn/unix.c   Sat Apr 13 01:39:58 2002
@@ -27,6 +27,7 @@
 #include <grp.h>
 #include <sys/time.h>
 #include <netinet/in.h>
+#include <libgen.h> /* For dirname().  */
 #ifdef HAVE_HURD_HURD_TYPES_H
 #include <hurd/hurd_types.h>
 #endif
@@ -145,6 +146,9 @@ struct get_stats_state
   int name_partial;            /* True if NAME isn't complete.  */
 
   int contents;                        /* Are we looking for directory 
contents?  */
+  char *searched_name;          /* If we are not, then we are only
+                                   looking for this name.  */
+
   int added_slash;             /* Did we prefix the name with `./'?  */
 
   struct stat stat;            /* Last read stat info.  */
@@ -155,8 +159,6 @@ struct get_stats_state
   char buf[7000];
 };
 
-
-
 /* Start an operation to get a list of file-stat structures for NAME (this is
    often similar to ftp_conn_start_dir, but with OS-specific flags), and
    return a file-descriptor for reading on, and a state structure in STATE
@@ -168,51 +170,97 @@ ftp_conn_unix_start_get_stats (struct ft
                               const char *name, int contents,
                               int *fd, void **state)
 {
-  error_t err;
+  error_t err = 0;
   size_t req_len;
-  char *req;
-  struct get_stats_state *s = malloc (sizeof (struct get_stats_state));
-  const char *flags = contents ? "-A" : "-Ad";
+  char *req = NULL;
+  struct get_stats_state *s = NULL;
+  const char *flags = "-A";
   const char *slash = strchr (name, '/');
+  char *searched_name = NULL;
 
+  s = (struct get_stats_state *) malloc (sizeof (struct get_stats_state));
   if (! s)
-    return ENOMEM;
+    {
+      err = ENOMEM;
+      goto out;
+    }
+  if (! contents)
+    {
+      if (! strcmp (name, "/"))
+       {
+         /* Listing only the directory itself and not the directory
+            content seems to be not supported by all FTP servers.  If
+            the directory in question is not the root directory, we
+            can simply lookup `..', but that doesn't work if we are
+            already on the root directory.  */
+         err = EINVAL;
+       }
+      else
+       {
+         searched_name = strdup (basename ((char *) name));
+         if (! searched_name)
+           err = ENOMEM;
+       }
+      if (err)
+       goto out;
+    }
 
   if (strcspn (name, "*? \t\n{}$`\\\"'") < strlen (name))
     /* NAME contains some metacharacters, which makes the behavior of various
        ftp servers unpredictable, so punt.  */
     {
-      free (s);
-      return EINVAL;
+      err = EINVAL;
+      goto out;
     }
 
   /* We pack the ls options and the name into the list argument, in REQ,
      which will do the right thing on most unix ftp servers.  */
 
-  /* Space needed for REQ.  */
-  req_len = strlen (flags) + 1 + strlen (name) + 1;
-
-  /* If NAME doesn't contain a slash, we prepend `./' to it so that we can
-     tell from the results whether it's a directory or not.  */
-  if (! slash)
-    req_len += 2;
-
-  req = malloc (req_len);
-  if (! req)
-    return ENOMEM;
-
-  snprintf (req, req_len, "%s %s%s", flags, slash ? "" : "./", name);
+  req_len = strlen (flags) + 2; /* space character + null character.  */
+  if (! contents)
+    {
+      char *dirn = dirname ((char *) name);
+      int is_root = ! strcmp (dirn, "/");
+      req_len += strlen (dirn) + (is_root ? 0 : 1);
+      req = malloc (req_len);
+      if (! req)
+       err = ENOMEM;
+      else
+       sprintf (req, "%s %s%s", flags, dirn, (is_root ? "" : "/"));
+    }
+  else
+    {
+      /* If NAME doesn't contain a slash, we prepend `./' to it so that we can
+        tell from the results whether it's a directory or not.  */
+      req_len += strlen (name) + (slash ? 0 : 2);
+      req = malloc (req_len);
+      if (! req)
+       err = ENOMEM;
+      else
+       sprintf (req, "%s %s%s", flags, slash ? "" : "./", name);
+    }
+  
+  if (err)
+    goto out;
 
   /* Make the actual request.  */
   err = ftp_conn_start_dir (conn, req, fd);
 
-  free (req);
+ out:
 
+  if (req)
+    free (req);
   if (err)
-    free (s);
+    {
+      if (s)
+       free (s);
+      if (searched_name)
+       free (searched_name);
+    }
   else
     {
       s->contents = contents;
+      s->searched_name = searched_name;
       s->added_slash = !slash;
       s->name = 0;
       s->name_len = s->name_alloced = 0;
@@ -616,11 +664,16 @@ ftp_conn_unix_cont_get_stats (struct ftp
          /* Pass only directory-relative names to the callback function.  */
          name = basename (name);
 
-         /* Call the callback function to process the current entry; it is
-            responsible for freeing S->name and SYMLINK_TARGET.  */
-         err = (*add_stat) (name, &s->stat, symlink_target, hook);
-         if (err)
-           goto finished;
+         if (s->contents || ! strcmp (s->name, s->searched_name))
+           {
+             /* We are only interested in searched_name.  */
+
+             /* Call the callback function to process the current entry; it is
+                responsible for freeing S->name and SYMLINK_TARGET.  */
+             err = (*add_stat) (name, &s->stat, symlink_target, hook);
+             if (err)
+               goto finished;
+           }
 
          s->name_len = 0;
          s->name_partial = 0;
@@ -658,6 +711,8 @@ finished:
      return.  */
   if (s->name)
     free (s->name);
+  if (s->searched_name)
+    free (s->searched_name);
   free (s);
   close (fd);


                moritz
-- 
moritz@duesseldorf.ccc.de - http://duesseldorf.ccc.de/~moritz/
GPG fingerprint = 3A14 3923 15BE FD57 FC06  B501 0841 2D7B 6F98 4199



reply via email to

[Prev in Thread] Current Thread [Next in Thread]