func (h *dbHashTree) DeleteFile(path string) error {
	path = clean(path)

	// Delete root means delete all files
	if path == "" {
		path = "/*"
	}
	return h.Batch(func(tx *bolt.Tx) error {
		if err := glob(tx, path, func(path string, node *NodeProto) error {
			// Check if the file has been deleted already
			if _, err := get(tx, path); err != nil && Code(err) == PathNotFound {
				return nil
			}
			// Remove 'path' and all nodes underneath it from h.fs
			if err := deleteDir(tx, path); err != nil {
				return err
			}
			size := node.SubtreeSize
			// Remove 'path' from its parent directory
			// TODO(bryce) Decide if this should be removed.
			parent, _ := split(path)
			pnode, err := get(tx, parent)
			if err != nil {
				if Code(err) == PathNotFound {
					return errorf(Internal, "delete discovered orphaned file \"%s\"", path)
				}
				return err
			}
			if pnode.DirNode == nil {
				return errorf(Internal, "file at \"%s\" is a regular-file, but \"%s\" already exists "+
					"under it (likely an uncaught PathConflict in prior PutFile or Merge)", path, pnode.DirNode)
			}
			put(tx, parent, pnode)
			// Mark nodes as 'changed' back to root
			if err := visit(tx, path, func(node *NodeProto, parent, child string) error {
				// If node.DirNode is nil it means either the parent didn't
				// exist (and thus was deserialized fron nil) or it does exist
				// but thinks it's a file, both are errors.
				if node.DirNode == nil {
					return errorf(Internal,
						"encountered orphaned file \"%s\" while deleting \"%s\"", path,
						join(parent, child))
				}
				node.SubtreeSize -= size
				return nil
			}); err != nil {
				return err
			}
			return nil
		}); err != nil && Code(err) != PathNotFound {
			// Deleting a non-existent file should be a no-op
			return err
		}
		return nil
	})
}
func (h *dbHashTree) DeleteFile(path string) error {
	path = clean(path)

	// Delete root means delete all files
	if path == "" {
		path = "/*"
	}
	return h.Batch(func(tx *bolt.Tx) error {
		if err := glob(tx, path, func(path string, node *NodeProto) error {
			// Check if the file has been deleted already
			if _, err := get(tx, path); err != nil && Code(err) == PathNotFound {
				return nil
			}
			// Remove 'path' and all nodes underneath it from h.fs
			if err := deleteDir(tx, path); err != nil {
				return err
			}
			size := node.SubtreeSize
			// Remove 'path' from its parent directory
			// TODO(bryce) Decide if this should be removed.
			parent, _ := split(path)
			pnode, err := get(tx, parent)
			if err != nil {
				if Code(err) == PathNotFound {
					return errorf(Internal, "delete discovered orphaned file \"%s\"", path)
				}
				return err
			}
			if pnode.DirNode == nil {
				return errorf(Internal, "file at \"%s\" is a regular-file, but \"%s\" already exists "+
					"under it (likely an uncaught PathConflict in prior PutFile or Merge)", path, pnode.DirNode)
			}
			put(tx, parent, pnode)
			// Mark nodes as 'changed' back to root
			if err := visit(tx, path, func(node *NodeProto, parent, child string) error {
				// If node.DirNode is nil it means either the parent didn't
				// exist (and thus was deserialized fron nil) or it does exist
				// but thinks it's a file, both are errors.
				if node.DirNode == nil {
					return errorf(Internal,
						"encountered orphaned file \"%s\" while deleting \"%s\"", path,
						join(parent, child))
				}
				node.SubtreeSize -= size
				return nil
			}); err != nil {
				return err
			}
			return nil
		}); err != nil && Code(err) != PathNotFound {
			// Deleting a non-existent file should be a no-op
			return err
		}
		return nil
	})
}
// DeleteFile deletes a regular file or directory (along with its children).