From 9214dbda50ff9d10ec638ceb1ba77aaa0e22331d Mon Sep 17 00:00:00 2001
From: david-sarah <david-sarah@jacaranda.org>
Date: Thu, 27 May 2010 12:45:29 -0700
Subject: [PATCH] Add must_exist, must_be_directory, and must_be_file arguments
 to DirectoryNode.delete. This will be used to fixes a minor condition in the
 SFTP frontend.

---
 src/allmydata/dirnode.py    | 21 ++++++++++++++++-----
 src/allmydata/interfaces.py | 11 ++++++++---
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py
index 8a485f41..bfc151a3 100644
--- a/src/allmydata/dirnode.py
+++ b/src/allmydata/dirnode.py
@@ -11,7 +11,7 @@ from allmydata.unknown import UnknownNode, strip_prefix_for_ro
 from allmydata.interfaces import IFilesystemNode, IDirectoryNode, IFileNode, \
      IImmutableFileNode, IMutableFileNode, \
      ExistingChildError, NoSuchChildError, ICheckable, IDeepCheckable, \
-     MustBeDeepImmutableError, CapConstraintError
+     MustBeDeepImmutableError, CapConstraintError, ChildOfWrongTypeError
 from allmydata.check_results import DeepCheckResults, \
      DeepCheckAndRepairResults
 from allmydata.monitor import Monitor
@@ -81,10 +81,13 @@ def update_metadata(metadata, new_metadata, now):
 # the unpacked contents.
 
 class Deleter:
-    def __init__(self, node, name, must_exist=True):
+    def __init__(self, node, name, must_exist=True, must_be_directory=False, must_be_file=False):
         self.node = node
         self.name = name
-        self.must_exist = True
+        self.must_exist = must_exist
+        self.must_be_directory = must_be_directory
+        self.must_be_file = must_be_file
+
     def modify(self, old_contents, servermap, first_time):
         children = self.node._unpack_contents(old_contents)
         if self.name not in children:
@@ -93,6 +96,13 @@ class Deleter:
             self.old_child = None
             return None
         self.old_child, metadata = children[self.name]
+
+        # Unknown children can be removed regardless of must_be_directory or must_be_file.
+        if self.must_be_directory and IFileNode.providedBy(self.old_child):
+            raise ChildOfWrongTypeError("delete required a directory, not a file")
+        if self.must_be_file and IDirectoryNode.providedBy(self.old_child):
+            raise ChildOfWrongTypeError("delete required a file, not a directory")
+
         del children[self.name]
         new_contents = self.node._pack_contents(children)
         return new_contents
@@ -568,13 +578,14 @@ class DirectoryNode:
                       self.set_node(name, node, metadata, overwrite))
         return d
 
-    def delete(self, name):
+    def delete(self, name, must_exist=True, must_be_directory=False, must_be_file=False):
         """I remove the child at the specific name. I return a Deferred that
         fires (with the node just removed) when the operation finishes."""
         assert isinstance(name, unicode)
         if self.is_readonly():
             return defer.fail(NotWriteableError())
-        deleter = Deleter(self, name)
+        deleter = Deleter(self, name, must_exist=must_exist,
+                          must_be_directory=must_be_directory, must_be_file=must_be_file)
         d = self._node.modify(deleter.modify)
         d.addCallback(lambda res: deleter.old_child)
         return d
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index 948fac03..98c3dbb1 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -828,6 +828,9 @@ class ExistingChildError(Exception):
 class NoSuchChildError(Exception):
     """A directory node was asked to fetch a child which does not exist."""
 
+class ChildOfWrongTypeError(Exception):
+    """An operation was attempted on a child of the wrong type (file or directory)."""
+
 class IDirectoryNode(IFilesystemNode):
     """I represent a filesystem node that is a container, with a
     name-to-child mapping, holding the tahoe equivalent of a directory. All
@@ -974,11 +977,13 @@ class IDirectoryNode(IFilesystemNode):
         I return a Deferred that fires (with the IFileNode of the uploaded
         file) when the operation completes."""
 
-    def delete(name):
+    def delete(name, must_exist=True, must_be_directory=False, must_be_file=False):
         """I remove the child at the specific name. I return a Deferred that
         fires when the operation finishes. The child name must be a unicode
-        string. I raise NoSuchChildError if I do not have a child by that
-        name."""
+        string. If must_exist is True and I do not have a child by that name,
+        I raise NoSuchChildError. If must_be_directory is True and the child
+        is a file, or if must_be_file is True and the child is a directory,
+        I raise ChildOfWrongTypeError."""
 
     def create_subdirectory(name, initial_children={}, overwrite=True):
         """I create and attach a directory at the given name. The new
-- 
2.45.2