func()

in internal/fs/fs.go [1919:2036]


func (fs *fileSystem) RmDir(
	// When rm -r or os.RemoveAll call is made, the following calls are made in order
	//	 1. RmDir (only in the case of os.RemoveAll)
	//	 2. Unlink all nested files,
	//	 3. lookupInode call on implicit directory
	//	 4. Rmdir on the directory.
	//
	// When type cache ttl is set, we construct an implicitDir even though one doesn't
	// exist on GCS (https://github.com/GoogleCloudPlatform/gcsfuse/blob/master/internal/fs/inode/dir.go#L452),
	// and thus, we get rmDir call to GCSFuse.
	// Whereas when ttl is zero, lookupInode call itself fails and RmDir is not called
	// because object is not present in GCS.

	ctx context.Context,
	op *fuseops.RmDirOp) (err error) {
	if fs.newConfig.FileSystem.IgnoreInterrupts {
		// When ignore interrupts config is set, we are creating a new context not
		// cancellable by parent context.
		var cancel context.CancelFunc
		ctx, cancel = util.IsolateContextFromParentContext(ctx)
		defer cancel()
	}
	// Find the parent.
	fs.mu.Lock()
	parent := fs.dirInodeOrDie(op.Parent)
	fs.mu.Unlock()

	// Find or create the child inode, locked.
	child, err := fs.lookUpOrCreateChildInode(ctx, parent, op.Name)
	if err != nil {
		return
	}

	// Set up a function that throws away the lookup count increment that we
	// implicitly did above (since we're not handing the child back to the
	// kernel) and unlocks the child, but only once. Ensure it is called at least
	// once in case we exit early.
	childCleanedUp := false
	cleanUpAndUnlockChild := func() {
		if !childCleanedUp {
			childCleanedUp = true
			fs.unlockAndDecrementLookupCount(child, 1)
		}
	}

	defer cleanUpAndUnlockChild()

	// Is the child a directory?
	childDir, ok := child.(inode.DirInode)
	if !ok {
		err = fuse.ENOTDIR
		return
	}

	// Ensure that the child directory is empty.
	//
	// Yes, this is not atomic with the delete below. See here for discussion:
	//
	//     https://github.com/GoogleCloudPlatform/gcsfuse/issues/9
	//
	//

	// Check for local file entries.
	fs.mu.Lock()
	localFileEntries := childDir.LocalFileEntries(fs.localFileInodes)
	fs.mu.Unlock()
	// Are there any local entries?
	if len(localFileEntries) != 0 {
		err = fuse.ENOTEMPTY
		return
	}

	// Check for entries on GCS.
	var tok string
	for {
		var entries []fuseutil.Dirent
		entries, tok, err = childDir.ReadEntries(ctx, tok)
		if err != nil {
			err = fmt.Errorf("ReadEntries: %w", err)
			return err
		}

		if fs.kernelListCacheTTL > 0 {
			// Clear kernel list cache after removing a directory. This ensures remote
			// GCS files are included in future directory listings for unlinking.
			childDir.InvalidateKernelListCache()
		}

		// Are there any entries?
		if len(entries) != 0 {
			err = fuse.ENOTEMPTY
			return
		}

		// Are we done listing?
		if tok == "" {
			break
		}
	}

	// We are done with the child.
	cleanUpAndUnlockChild()

	// Delete the backing object.
	fs.mu.Lock()
	_, isImplicitDir := fs.implicitDirInodes[child.Name()]
	fs.mu.Unlock()
	parent.Lock()
	err = parent.DeleteChildDir(ctx, op.Name, isImplicitDir, childDir)
	parent.Unlock()

	if err != nil {
		err = fmt.Errorf("DeleteChildDir: %w", err)
		return err
	}

	return
}