changeset 852:5e3005923374

2006-11-16 Brian Masney <masneyb@gftp.org> * lib/gftp.h lib/protocols.c lib/local.c - added support for detecting recursive symbolic links. This currently only works when uploading files. It will only work when downloading files if the inode and device are properly populated. I need to check but it may be possible to get this information from the SSH and FSP protocols. (closes #172499)
author masneyb
date Fri, 17 Nov 2006 02:24:14 +0000
parents 83d39217749c
children e30271d47353
files ChangeLog lib/gftp.h lib/local.c lib/misc.c lib/protocols.c
diffstat 5 files changed, 102 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Nov 17 01:38:51 2006 +0000
+++ b/ChangeLog	Fri Nov 17 02:24:14 2006 +0000
@@ -1,4 +1,13 @@
 2006-11-16 Brian Masney <masneyb@gftp.org>
+	* lib/gftp.h lib/protocols.c lib/local.c - added support for detecting
+	recursive symbolic links. This currently only works when uploading
+	files. It will only work when downloading files if the inode and device
+	are properly populated. I need to check but it may be possible to get
+	this information from the SSH and FSP protocols. (closes #172499)
+
+	* lib/local.c lib/misc.c - moved uint_hash_compare() and
+	uint_hash_function() from local.c to misc.c 
+
 	* lib/protocols.c - backed out last change where hidden files aren't
 	transferred in the subdirectories if the show_hidden_files option is
 	disabled.
@@ -3715,7 +3724,7 @@
 
 	* cvsclean - added this script
 
-	* *.[ch] - added $Id: ChangeLog,v 1.500 2006/11/17 01:38:50 masneyb Exp $ tags
+	* *.[ch] - added $Id: ChangeLog,v 1.501 2006/11/17 02:24:03 masneyb Exp $ tags
 
 	* debian/* - updated files from Debian maintainer
 
--- a/lib/gftp.h	Fri Nov 17 01:38:51 2006 +0000
+++ b/lib/gftp.h	Fri Nov 17 02:24:14 2006 +0000
@@ -244,6 +244,11 @@
   off_t size,			/* Size of the file */
         startsize;		/* Size to start the transfer at */
   mode_t st_mode;		/* File attributes */
+
+  dev_t st_dev;			/* The device and associated inode. These */
+  ino_t st_ino;			/* two fields are used for detecting loops */
+				/* with symbolic links. */
+
   unsigned int selected : 1,	/* Is this file selected? */
                was_sel : 1,	/* Was this file selected before  */
                shown : 1,	/* Is this file shown? */
@@ -753,6 +758,11 @@
 
 guint string_hash_function		( gconstpointer key );
 
+gint uint_hash_compare 			( gconstpointer path1,
+					  gconstpointer path2 );
+
+guint uint_hash_function		( gconstpointer key );
+
 void free_file_list			( GList * filelist );
 
 gftp_file * copy_fdata 			( gftp_file * fle );
--- a/lib/local.c	Fri Nov 17 01:38:51 2006 +0000
+++ b/lib/local.c	Fri Nov 17 02:24:14 2006 +0000
@@ -382,6 +382,8 @@
       g_hash_table_insert (lpd->grouphash, GUINT_TO_POINTER (st.st_gid), group);
     }
 
+  fle->st_dev = fst.st_dev;
+  fle->st_ino = fst.st_ino;
   fle->st_mode = fst.st_mode;
   fle->datetime = st.st_mtime;
 
@@ -696,20 +698,6 @@
 }
 
 
-static gint
-hash_compare (gconstpointer path1, gconstpointer path2)
-{
-  return (GPOINTER_TO_UINT (path1) == GPOINTER_TO_UINT (path2));
-}
-
-
-static guint
-hash_function (gconstpointer key)
-{
-  return (GPOINTER_TO_UINT (key));
-}
-
-
 void 
 local_register_module (void)
 {
@@ -765,8 +753,8 @@
 
   lpd = g_malloc0 (sizeof (*lpd));
   request->protocol_data = lpd;
-  lpd->userhash = g_hash_table_new (hash_function, hash_compare);
-  lpd->grouphash = g_hash_table_new (hash_function, hash_compare);
+  lpd->userhash = g_hash_table_new (uint_hash_function, uint_hash_compare);
+  lpd->grouphash = g_hash_table_new (uint_hash_function, uint_hash_compare);
 
   if (request->hostname != NULL)
     g_free (request->hostname);
--- a/lib/misc.c	Fri Nov 17 01:38:51 2006 +0000
+++ b/lib/misc.c	Fri Nov 17 02:24:14 2006 +0000
@@ -430,6 +430,20 @@
 }
 
 
+gint
+uint_hash_compare (gconstpointer path1, gconstpointer path2)
+{
+  return (GPOINTER_TO_UINT (path1) == GPOINTER_TO_UINT (path2));
+}
+
+
+guint
+uint_hash_function (gconstpointer key)
+{
+  return (GPOINTER_TO_UINT (key));
+}
+
+
 void
 free_file_list (GList * filelist)
 {
--- a/lib/protocols.c	Fri Nov 17 01:38:51 2006 +0000
+++ b/lib/protocols.c	Fri Nov 17 02:24:14 2006 +0000
@@ -2077,12 +2077,66 @@
 }
 
 
+static int
+_lookup_curfle_in_device_hash (gftp_request * request, gftp_file * curfle,
+                               GHashTable * device_hash)
+{
+  GHashTable * inode_hash;
+
+  if (curfle->st_dev == 0 || curfle->st_ino == 0)
+    return (0);
+
+  if ((inode_hash = g_hash_table_lookup (device_hash,
+                                         GUINT_TO_POINTER ((guint) curfle->st_dev))) != NULL)
+    {
+      if (g_hash_table_lookup (inode_hash,
+                               GUINT_TO_POINTER ((guint) curfle->st_ino)))
+        {
+          request->logging_function (gftp_logging_error, request,
+                                     _("Found recursive symbolic link %s\n"),
+                                     curfle->file);
+          return (1);
+        }
+
+      g_hash_table_insert (inode_hash, GUINT_TO_POINTER ((guint) curfle->st_ino),
+                           GUINT_TO_POINTER (1));
+      return (0);
+    }
+  else
+    {
+      inode_hash = g_hash_table_new (uint_hash_function, uint_hash_compare);
+      g_hash_table_insert (inode_hash, GUINT_TO_POINTER ((guint) curfle->st_ino),
+                           GUINT_TO_POINTER (1));
+      g_hash_table_insert (device_hash, GUINT_TO_POINTER ((guint) curfle->st_dev),
+                           inode_hash);
+      return (0);
+    }
+
+}
+
+
+static void
+_free_inode_hash (gpointer key, gpointer value, gpointer user_data)
+{
+  g_hash_table_destroy (value);
+}
+
+
+static void
+_free_device_hash (GHashTable * device_hash)
+{
+  g_hash_table_foreach (device_hash, _free_inode_hash, NULL);
+  g_hash_table_destroy (device_hash);
+}
+
+
 int
 gftp_get_all_subdirs (gftp_transfer * transfer,
                       void (*update_func) (gftp_transfer * transfer))
 {
   GList * templist, * lastlist;
   char *oldfromdir, *oldtodir;
+  GHashTable * device_hash;
   gftp_file * curfle;
   off_t linksize;
   mode_t st_mode;
@@ -2101,11 +2155,16 @@
     return (ret);
 
   oldfromdir = oldtodir = NULL;
+  device_hash = g_hash_table_new (uint_hash_function, uint_hash_compare);
 
   for (templist = transfer->files; templist != NULL; templist = templist->next)
     {
       curfle = templist->data;
 
+      if (_lookup_curfle_in_device_hash (transfer->fromreq, curfle,
+                                         device_hash))
+        continue;
+
       if (S_ISLNK (curfle->st_mode) && !S_ISDIR (curfle->st_mode))
         {
           st_mode = 0;
@@ -2141,6 +2200,7 @@
         {
           _cleanup_get_all_subdirs (transfer, oldfromdir, oldtodir,
                                     update_func);
+          _free_device_hash (device_hash);
           return (ret);
         }
 
@@ -2156,6 +2216,7 @@
                 {
                   _cleanup_get_all_subdirs (transfer, oldfromdir, oldtodir,
                                             update_func);
+                  _free_device_hash (device_hash);
                   return (ret);
                 }
             }
@@ -2175,6 +2236,7 @@
         {
           _cleanup_get_all_subdirs (transfer, oldfromdir, oldtodir,
                                     update_func);
+          _free_device_hash (device_hash);
           return (ret);
         }
 
@@ -2188,6 +2250,8 @@
         update_func (transfer);
     }
 
+  _free_device_hash (device_hash);
+
   if (oldfromdir != NULL)
     {
       ret = gftp_set_directory (transfer->fromreq, oldfromdir);