From e58850eb05b237e79f53b779d801d7f880fa6cc7 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Dec 19 2016 17:28:10 +0000
Subject: [PATCH 1/5] first stab at rmtree for long filenames


---

diff --git a/koji/util.py b/koji/util.py
index 563703d..e16a6a0 100644
--- a/koji/util.py
+++ b/koji/util.py
@@ -319,6 +319,56 @@ def rmtree(path):
         os.rmdir(dirpath)
 
 
+def rmtree2(path):
+    """Reimplement to avoid forming long paths"""
+    st = os.lstat(path)
+    if not stat.S_ISDIR(st.st_mode):
+        raise koji.GenericError, "Not a directory: %s" % path
+    dev = st.st_dev
+    cwd = os.getcwd()
+    try:
+        os.chdir(path)
+        _rmtree2(dev)
+    finally:
+        os.chdir(cwd)
+    os.rmdir(path)
+
+
+def _rmtree2(dev):
+    dirstack = []
+    while True:
+        dirs = _stripcwd(dev)
+        # if no dirs, walk back up until we find some
+        while not dirs and dirstack:
+            os.chdir('..')
+            dirs = dirstack.pop()
+            empty_dir = dirs.pop()
+            os.rmdir(empty_dir)
+        if not dirs:
+            # we are done
+            break
+        # otherwise go deeper
+        subdir = dirs[-1]
+        # note: we do not pop here because we need to remember to remove subdir later
+        dirstack.append(dirs)
+        os.chdir(subdir)
+
+
+def _stripcwd(dev):
+    """Unlink all files in cwd and return list of subdirs"""
+    dirs = []
+    for fn in os.listdir('.'):
+        st = os.lstat(fn)
+        if st.st_dev != dev:
+            # don't cross fs boundary
+            continue
+        if stat.S_ISDIR(st.st_mode):
+            dirs.append(fn)
+        else:
+            os.unlink(fn)
+    return dirs
+
+
 def safer_move(src, dst):
     """Rename if possible, copy+rm otherwise
 

From 949f23dd7b11021e761b8d0bf7230c78504fef58 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Dec 19 2016 17:28:10 +0000
Subject: [PATCH 2/5] add a note


---

diff --git a/koji/util.py b/koji/util.py
index e16a6a0..788a19c 100644
--- a/koji/util.py
+++ b/koji/util.py
@@ -320,7 +320,9 @@ def rmtree(path):
 
 
 def rmtree2(path):
-    """Reimplement to avoid forming long paths"""
+    """Delete a directory tree without crossing fs boundaries"""
+    # implemented to avoid forming long paths
+    # see: https://pagure.io/koji/issue/201
     st = os.lstat(path)
     if not stat.S_ISDIR(st.st_mode):
         raise koji.GenericError, "Not a directory: %s" % path

From 8f42fda97de52afc9ea0c683ff9e853a2a8a930a Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Dec 19 2016 17:28:10 +0000
Subject: [PATCH 3/5] keep going on errors


---

diff --git a/koji/util.py b/koji/util.py
index 788a19c..95f63bf 100644
--- a/koji/util.py
+++ b/koji/util.py
@@ -345,7 +345,11 @@ def _rmtree2(dev):
             os.chdir('..')
             dirs = dirstack.pop()
             empty_dir = dirs.pop()
-            os.rmdir(empty_dir)
+            try:
+                os.rmdir(empty_dir)
+            except OSError:
+                # we'll still fail at the top level
+                pass
         if not dirs:
             # we are done
             break
@@ -367,7 +371,11 @@ def _stripcwd(dev):
         if stat.S_ISDIR(st.st_mode):
             dirs.append(fn)
         else:
-            os.unlink(fn)
+            try:
+                os.unlink(fn)
+            except OSError:
+                # we'll still fail at the top level
+                pass
     return dirs
 
 

From d4590637a1cc08edf33c2e453e28b72ed6abcbf4 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Dec 19 2016 17:28:10 +0000
Subject: [PATCH 4/5] replace old rmtree with the new one


---

diff --git a/koji/util.py b/koji/util.py
index 95f63bf..3091002 100644
--- a/koji/util.py
+++ b/koji/util.py
@@ -282,45 +282,6 @@ def lazysetattr(object, name, func, args, kwargs=None, cache=False):
 
 def rmtree(path):
     """Delete a directory tree without crossing fs boundaries"""
-    st = os.lstat(path)
-    if not stat.S_ISDIR(st.st_mode):
-        raise koji.GenericError, "Not a directory: %s" % path
-    dev = st.st_dev
-    dirlist = []
-    for dirpath, dirnames, filenames in os.walk(path):
-        dirlist.append(dirpath)
-        newdirs = []
-        dirsyms = []
-        for fn in dirnames:
-            path = os.path.join(dirpath, fn)
-            st = os.lstat(path)
-            if st.st_dev != dev:
-                # don't cross fs boundary
-                continue
-            if stat.S_ISLNK(st.st_mode):
-                #os.walk includes symlinks to dirs here
-                dirsyms.append(fn)
-                continue
-            newdirs.append(fn)
-        #only walk our filtered dirs
-        dirnames[:] = newdirs
-        for fn in filenames + dirsyms:
-            path = os.path.join(dirpath, fn)
-            st = os.lstat(path)
-            if st.st_dev != dev:
-                #shouldn't happen, but just to be safe...
-                continue
-            os.unlink(path)
-    dirlist.reverse()
-    for dirpath in dirlist:
-        if os.listdir(dirpath):
-            # dir not empty. could happen if a mount was present
-            continue
-        os.rmdir(dirpath)
-
-
-def rmtree2(path):
-    """Delete a directory tree without crossing fs boundaries"""
     # implemented to avoid forming long paths
     # see: https://pagure.io/koji/issue/201
     st = os.lstat(path)
@@ -330,13 +291,13 @@ def rmtree2(path):
     cwd = os.getcwd()
     try:
         os.chdir(path)
-        _rmtree2(dev)
+        _rmtree(dev)
     finally:
         os.chdir(cwd)
     os.rmdir(path)
 
 
-def _rmtree2(dev):
+def _rmtree(dev):
     dirstack = []
     while True:
         dirs = _stripcwd(dev)

From ab99443414918f19417d5ee5d8616ec3838f3623 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Dec 19 2016 17:31:01 +0000
Subject: [PATCH 5/5] Update exception syntax in rmtree()


---

diff --git a/koji/util.py b/koji/util.py
index 3091002..50d203d 100644
--- a/koji/util.py
+++ b/koji/util.py
@@ -286,7 +286,7 @@ def rmtree(path):
     # see: https://pagure.io/koji/issue/201
     st = os.lstat(path)
     if not stat.S_ISDIR(st.st_mode):
-        raise koji.GenericError, "Not a directory: %s" % path
+        raise koji.GenericError("Not a directory: %s" % path)
     dev = st.st_dev
     cwd = os.getcwd()
     try:

