You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
788 lines
19 KiB
788 lines
19 KiB
/*
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
nwtrash.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the routines for performing Network Trash Folder
|
|
operations.
|
|
|
|
Author:
|
|
|
|
Sue Adams (microsoft!suea)
|
|
|
|
|
|
Revision History:
|
|
06 Aug 1992 Initial Version
|
|
|
|
Notes: Tab stop: 4
|
|
--*/
|
|
|
|
#define NWTRASH_LOCALS
|
|
#define FILENUM FILE_NWTRASH
|
|
|
|
#include <afp.h>
|
|
#include <fdparm.h>
|
|
#include <pathmap.h>
|
|
#include <nwtrash.h>
|
|
#include <afpinfo.h>
|
|
#include <access.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfpCreateNetworkTrash)
|
|
#pragma alloc_text( PAGE, AfpDeleteNetworkTrash)
|
|
#pragma alloc_text( PAGE, afpCleanNetworkTrash)
|
|
#pragma alloc_text( PAGE, AfpWalkDirectoryTree)
|
|
#pragma alloc_text( PAGE, afpPushDirNode)
|
|
#pragma alloc_text( PAGE, afpPopDirNode)
|
|
#pragma alloc_text( PAGE, AfpGetNextDirectoryInfo)
|
|
#pragma alloc_text( PAGE, afpNwtDeleteFileEntity)
|
|
#endif
|
|
|
|
/*** AfpCreateNetworkTrash
|
|
*
|
|
* Create the network trash folder for a newly added volume.
|
|
* Make sure it is hidden and make sure the streams are intact.
|
|
* This routine may only be called for NTFS volumes. Note that even
|
|
* ReadOnly NTFS volumes will have a trash created. This is because
|
|
* if someone is going to toggle the volume ReadOnly bit, we don't need
|
|
* to worry about creating/deleting the trash on the fly.
|
|
* We keep an open handle to the network trash stored in the volume
|
|
* descriptor so that nobody can come in behind our backs and delete
|
|
* it.
|
|
*/
|
|
NTSTATUS
|
|
AfpCreateNetworkTrash(
|
|
IN PVOLDESC pVolDesc
|
|
)
|
|
{
|
|
FILESYSHANDLE hNWT;
|
|
PDFENTRY pDfEntry;
|
|
NTSTATUS Status;
|
|
ULONG info, Attr;
|
|
AFPINFO afpInfo;
|
|
BOOLEAN ReleaseSwmr = False;
|
|
PISECURITY_DESCRIPTOR pSecDesc;
|
|
FILEDIRPARM fdparm; // This is used to set the hidden attribute
|
|
// of the FinderInfo for network trash folder
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_IDINDEX,DBG_LEVEL_INFO,("\tCreating Network Trash...\n"));
|
|
ASSERT(pVolDesc->vds_Flags & VOLUME_NTFS);
|
|
|
|
hNWT.fsh_FileHandle = NULL;
|
|
|
|
Status = AfpMakeSecurityDescriptorForUser(&AfpSidWorld, &AfpSidWorld, &pSecDesc);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
ASSERT (pSecDesc != NULL);
|
|
ASSERT (pSecDesc->Dacl != NULL);
|
|
|
|
do
|
|
{
|
|
// NOTE: NTFS allows me to open a Readonly directory for
|
|
// delete access.
|
|
Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
|
|
AFP_STREAM_DATA,
|
|
&AfpNetworkTrashNameU,
|
|
AFP_NWT_ACCESS | AFP_OWNER_ACCESS,
|
|
AFP_NWT_SHAREMODE,
|
|
AFP_NWT_OPTIONS,
|
|
AFP_NWT_DISPOSITION,
|
|
AFP_NWT_ATTRIBS, // makes it hidden
|
|
False,
|
|
pSecDesc,
|
|
&hNWT,
|
|
&info,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Free the memory allocated for the security descriptor
|
|
AfpFreeMemory(pSecDesc->Dacl);
|
|
AfpFreeMemory(pSecDesc);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
ASSERT(info == FILE_CREATED);
|
|
|
|
// Add the AfpInfo stream
|
|
Status = AfpSlapOnAfpInfoStream(NULL,
|
|
NULL,
|
|
&hNWT,
|
|
NULL,
|
|
AFP_ID_NETWORK_TRASH,
|
|
True,
|
|
NULL,
|
|
&afpInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
// it does not exist in the ID index database, add it
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
|
ReleaseSwmr = True;
|
|
|
|
ASSERT(pVolDesc->vds_pDfeRoot != NULL);
|
|
|
|
pDfEntry = AfpAddDfEntry(pVolDesc,
|
|
pVolDesc->vds_pDfeRoot,
|
|
&AfpNetworkTrashNameU,
|
|
True,
|
|
AFP_ID_NETWORK_TRASH);
|
|
|
|
if (pDfEntry == NULL)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
// NOTE: pDfEntry now points to the "Network Trash Folder"
|
|
|
|
// Get directory info to cache
|
|
Status = AfpIoQueryTimesnAttr(&hNWT,
|
|
&pDfEntry->dfe_CreateTime,
|
|
&pDfEntry->dfe_LastModTime,
|
|
&Attr);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
ASSERT(Attr & FILE_ATTRIBUTE_HIDDEN);
|
|
pDfEntry->dfe_NtAttr = (USHORT)Attr & FILE_ATTRIBUTE_VALID_FLAGS;
|
|
pDfEntry->dfe_AfpAttr = afpInfo.afpi_Attributes;
|
|
pDfEntry->dfe_FinderInfo = afpInfo.afpi_FinderInfo;
|
|
pDfEntry->dfe_BackupTime = afpInfo.afpi_BackupTime;
|
|
DFE_OWNER_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
|
|
DFE_GROUP_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
|
|
DFE_WORLD_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
|
|
|
|
// ok, we know it now exists both on disk and in the database
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
RtlZeroMemory(&fdparm, sizeof(fdparm));
|
|
fdparm._fdp_Flags = DFE_FLAGS_DIR;
|
|
fdparm._fdp_AfpId = AFP_ID_NETWORK_TRASH;
|
|
fdparm._fdp_FinderInfo = afpInfo.afpi_FinderInfo;
|
|
|
|
// We must set the invisible flag in the finder info, because
|
|
// System 6 seems to ignore the hidden attribute.
|
|
pDfEntry->dfe_FinderInfo.fd_Attr1 |= FINDER_FLAG_INVISIBLE;
|
|
fdparm._fdp_FinderInfo.fd_Attr1 |= FINDER_FLAG_INVISIBLE;
|
|
Status = AfpSetAfpInfo(&hNWT,
|
|
FD_BITMAP_FINDERINFO,
|
|
&fdparm,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
} while (False);
|
|
|
|
if (hNWT.fsh_FileHandle != NULL)
|
|
AfpIoClose(&hNWT);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
AFPLOG_ERROR(AFPSRVMSG_CREATE_NWTRASH,
|
|
Status,
|
|
NULL,
|
|
0,
|
|
&pVolDesc->vds_Name);
|
|
}
|
|
else
|
|
{
|
|
// Open a Network Trash handle to keep around so that no one can
|
|
// come in and delete the Network Trash dir out from under us
|
|
Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
|
|
AFP_STREAM_DATA,
|
|
AFP_NWT_OPTIONS,
|
|
&AfpNetworkTrashNameU,
|
|
FILEIO_ACCESS_READ,
|
|
AFP_NWT_SHAREMODE,
|
|
False,
|
|
&pVolDesc->vds_hNWT);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
AFPLOG_ERROR(AFPSRVMSG_CREATE_NWTRASH, Status, NULL, 0,
|
|
&pVolDesc->vds_Name);
|
|
}
|
|
}
|
|
|
|
if (ReleaseSwmr)
|
|
{
|
|
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*** AfpDeleteNetworkTrash
|
|
*
|
|
* Delete the network trash folder from disk when a volume is being added,
|
|
* deleted or stopped.
|
|
*
|
|
* NOTE: this must be called in the server's context to ensure that we have
|
|
* LOCAL_SYSTEM access to all the trash directories created by users
|
|
*/
|
|
NTSTATUS
|
|
AfpDeleteNetworkTrash(
|
|
IN PVOLDESC pVolDesc,
|
|
IN BOOLEAN VolumeStart // Is volume starting or is it stopping
|
|
)
|
|
{
|
|
FILESYSHANDLE hNWT;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
|
|
("\tDeleting Network Trash...\n") );
|
|
ASSERT(pVolDesc->vds_Flags & VOLUME_NTFS);
|
|
|
|
if (!VolumeStart)
|
|
{
|
|
// Close the handle to Network Trash that we keep open so PC users can't
|
|
// delete the directory out from under us.
|
|
if (pVolDesc->vds_hNWT.fsh_FileHandle != NULL)
|
|
{
|
|
AfpIoClose(&pVolDesc->vds_hNWT);
|
|
pVolDesc->vds_hNWT.fsh_FileHandle = NULL;
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
|
|
|
// Open for delete access
|
|
Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
|
|
AFP_STREAM_DATA,
|
|
AFP_NWT_OPTIONS,
|
|
&AfpNetworkTrashNameU,
|
|
AFP_NWT_ACCESS,
|
|
AFP_NWT_SHAREMODE,
|
|
False,
|
|
&hNWT);
|
|
// there is no network trash folder to delete
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = afpCleanNetworkTrash(pVolDesc, &hNWT, NULL);
|
|
|
|
if (NT_SUCCESS(Status) || !VolumeStart)
|
|
{
|
|
|
|
// NOTE: NTFS will allow me to open the directory for
|
|
// DELETE access if it is marked Readonly, but I cannot delete it.
|
|
// Clear the Readonly Bit on the Network Trash Folder
|
|
AfpIoSetTimesnAttr(&hNWT,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
FILE_ATTRIBUTE_READONLY,
|
|
NULL,
|
|
NULL);
|
|
Status = AfpIoMarkFileForDelete(&hNWT,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
}
|
|
|
|
AfpIoClose(&hNWT);
|
|
}
|
|
} while (False);
|
|
|
|
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
|
|
|
if ((!NT_SUCCESS(Status)) && (Status != STATUS_OBJECT_NAME_NOT_FOUND))
|
|
{
|
|
AFPLOG_ERROR(AFPSRVMSG_DELETE_NWTRASH,
|
|
Status,
|
|
NULL,
|
|
0,
|
|
&pVolDesc->vds_Name);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*** afpCleanNetworkTrash
|
|
*
|
|
* Delete the contents of the network trash folder referenced by hNWT.
|
|
* If pDfeNWT is non-null, then delete the file/dir entries from the IdIndex
|
|
* database. If pDfeNWT is null, the volume is being deleted and the
|
|
* IdIndex database is getting blown away too, so don't bother removing the
|
|
* entries.
|
|
*/
|
|
LOCAL
|
|
NTSTATUS
|
|
afpCleanNetworkTrash(
|
|
IN PVOLDESC pVolDesc,
|
|
IN PFILESYSHANDLE phNWT,
|
|
IN PDFENTRY pDfeNWT OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_IDINDEX,DBG_LEVEL_INFO,("afpCleanNetworkTrash entered\n"));
|
|
|
|
if (pDfeNWT != NULL)
|
|
{
|
|
ASSERT(pDfeNWT->dfe_AfpId == AFP_ID_NETWORK_TRASH);
|
|
// clean out all child DFEntries belonging to the network trash
|
|
AfpPruneIdDb(pVolDesc,pDfeNWT);
|
|
}
|
|
|
|
Status = AfpWalkDirectoryTree(phNWT, afpNwtDeleteFileEntity);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AfpWalkDirectoryTree(
|
|
IN PFILESYSHANDLE phTargetDir,
|
|
IN WALKDIR_WORKER NodeWorker
|
|
)
|
|
{
|
|
PFILE_DIRECTORY_INFORMATION tmpptr;
|
|
PWALKDIR_NODE DirNodeStacktop = NULL, pcurrentnode;
|
|
NTSTATUS rc, status = STATUS_SUCCESS;
|
|
PBYTE enumbuf;
|
|
PWCHAR nodename;
|
|
ULONG nodenamelen;
|
|
BOOLEAN isdir;
|
|
UNICODE_STRING udirname;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// allocate the buffer that will hold enumerated files and dirs
|
|
//
|
|
if ((enumbuf = (PBYTE)AfpAllocPANonPagedMemory(AFP_ENUMBUF_SIZE)) == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
do // error handling loop
|
|
{
|
|
//
|
|
// prime the pump with the top level (target dir) directory handle
|
|
//
|
|
if ((rc = afpPushDirNode(&DirNodeStacktop, NULL, NULL))
|
|
!= STATUS_SUCCESS)
|
|
{
|
|
status = rc;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DirNodeStacktop->wdn_Handle = *phTargetDir;
|
|
}
|
|
|
|
//
|
|
// keep popping enumerated directories off the stack until stack empty
|
|
//
|
|
while ((pcurrentnode = DirNodeStacktop) != NULL)
|
|
{
|
|
if (pcurrentnode->wdn_Enumerated == False)
|
|
{
|
|
//
|
|
// get a handle to the directory so it can be enumerated
|
|
//
|
|
if (pcurrentnode->wdn_Handle.fsh_FileHandle == NULL)
|
|
{
|
|
RtlInitUnicodeString(&udirname,
|
|
pcurrentnode->wdn_RelativePath.Buffer);
|
|
// open a handle to the thing relative to the phTargetDir
|
|
rc = AfpIoOpen(phTargetDir,
|
|
AFP_STREAM_DATA,
|
|
FILEIO_OPEN_DIR,
|
|
&udirname,
|
|
FILEIO_ACCESS_READ,
|
|
FILEIO_DENY_NONE,
|
|
False,
|
|
&pcurrentnode->wdn_Handle);
|
|
|
|
if (!NT_SUCCESS(rc))
|
|
{
|
|
status = rc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// keep enumerating till we get all the entries
|
|
//
|
|
while (True)
|
|
{
|
|
rc = AfpIoQueryDirectoryFile(&pcurrentnode->wdn_Handle,
|
|
(PFILE_DIRECTORY_INFORMATION)enumbuf,
|
|
AFP_ENUMBUF_SIZE,
|
|
FileDirectoryInformation,
|
|
False, // return multiple entries
|
|
False, // don't restart scan
|
|
NULL);
|
|
|
|
ASSERT(rc != STATUS_PENDING);
|
|
|
|
if ((rc == STATUS_NO_MORE_FILES) ||
|
|
(rc == STATUS_NO_SUCH_FILE))
|
|
{
|
|
pcurrentnode->wdn_Enumerated = True;
|
|
break; // that's it, we've seen everything there is
|
|
}
|
|
//
|
|
// NOTE: if we get STATUS_BUFFER_OVERFLOW, the IO status
|
|
// information field does NOT tell us the required size
|
|
// of the buffer, so we wouldn't know how big to realloc
|
|
// the enum buffer if we wanted to retry, so don't bother
|
|
else if (!NT_SUCCESS(rc))
|
|
{
|
|
status = rc;
|
|
break; // enumerate failed, bail out
|
|
}
|
|
|
|
//
|
|
// process the enumerated files and dirs
|
|
//
|
|
tmpptr = (PFILE_DIRECTORY_INFORMATION)enumbuf;
|
|
while (True)
|
|
{
|
|
rc = AfpGetNextDirectoryInfo(&tmpptr,
|
|
&nodename,
|
|
&nodenamelen,
|
|
&isdir);
|
|
|
|
if (rc == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (isdir == True)
|
|
{
|
|
AfpInitUnicodeStringWithNonNullTerm(&udirname,
|
|
(USHORT)nodenamelen,
|
|
nodename);
|
|
|
|
if (RtlEqualUnicodeString(&Dot,&udirname,False) ||
|
|
RtlEqualUnicodeString(&DotDot,&udirname,False))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// push it onto the dir node stack
|
|
//
|
|
rc = afpPushDirNode(&DirNodeStacktop,
|
|
&pcurrentnode->wdn_RelativePath,
|
|
&udirname);
|
|
if (rc != STATUS_SUCCESS)
|
|
{
|
|
status = rc;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// its a file, call worker with its relative handle
|
|
// and path
|
|
//
|
|
rc = NodeWorker(&pcurrentnode->wdn_Handle,
|
|
nodename,
|
|
nodenamelen,
|
|
False);
|
|
if (!NT_SUCCESS(rc))
|
|
{
|
|
status = rc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // while more entries in the enumbuf
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
} // while there are more files to enumerate
|
|
|
|
if (pcurrentnode->wdn_Handle.fsh_FileHandle != phTargetDir->fsh_FileHandle)
|
|
{
|
|
AfpIoClose(&pcurrentnode->wdn_Handle);
|
|
}
|
|
}
|
|
else // we have already enumerated this directory
|
|
{
|
|
if (pcurrentnode->wdn_RelativePath.Length != 0)
|
|
{
|
|
// call the worker routine on this directory node
|
|
rc = NodeWorker(phTargetDir,
|
|
pcurrentnode->wdn_RelativePath.Buffer,
|
|
pcurrentnode->wdn_RelativePath.Length,
|
|
True);
|
|
}
|
|
else rc = STATUS_SUCCESS;
|
|
|
|
afpPopDirNode(&DirNodeStacktop);
|
|
if (!NT_SUCCESS(rc))
|
|
{
|
|
status = rc;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
} // while there are directories to pop
|
|
|
|
} while (False); // error handling loop
|
|
|
|
while (DirNodeStacktop != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
|
|
("AfpWalkDirectoryTree: WARNING: cleaning up dir stack\n") );
|
|
// clean up in case of error
|
|
afpPopDirNode(&DirNodeStacktop);
|
|
}
|
|
AfpFreePANonPagedMemory(enumbuf, AFP_ENUMBUF_SIZE);
|
|
return status;
|
|
}
|
|
|
|
/*** afpPushDirNode
|
|
*
|
|
* Keep a record of all the directories we have encountered so far during
|
|
* enumeration of the tree. We need to process directories from the
|
|
* bottom up because the WalkTree node worker routine does a delete
|
|
* on all the items in a tree, and we certainly cant be deleting directories that
|
|
* are not empty.
|
|
*
|
|
*/
|
|
LOCAL
|
|
NTSTATUS
|
|
afpPushDirNode(
|
|
IN OUT PWALKDIR_NODE *ppStacktop,
|
|
IN PUNICODE_STRING pParentPath, // path to parent (NULL iff walk root)
|
|
IN PUNICODE_STRING pDirName // name of current node directory
|
|
)
|
|
{
|
|
PWALKDIR_NODE tempptr;
|
|
UNICODE_STRING ubackslash;
|
|
ULONG memsize;
|
|
USHORT namesize = 0;
|
|
|
|
PAGED_CODE( );
|
|
|
|
if (pParentPath != NULL)
|
|
{
|
|
namesize = pParentPath->Length + sizeof(WCHAR) +
|
|
pDirName->Length + sizeof(UNICODE_NULL);
|
|
}
|
|
memsize = namesize + sizeof(WALKDIR_NODE);
|
|
|
|
if ((tempptr = (PWALKDIR_NODE)AfpAllocNonPagedMemory(memsize)) == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
tempptr->wdn_Enumerated = False;
|
|
tempptr->wdn_Handle.fsh_FileHandle = NULL;
|
|
tempptr->wdn_RelativePath.Length = 0;
|
|
tempptr->wdn_RelativePath.MaximumLength = namesize;
|
|
|
|
if (pParentPath != NULL)
|
|
{
|
|
tempptr->wdn_RelativePath.Buffer = (LPWSTR)((PBYTE)tempptr +
|
|
sizeof(WALKDIR_NODE));
|
|
if (pParentPath->Length != 0)
|
|
{
|
|
RtlInitUnicodeString(&ubackslash,L"\\");
|
|
AfpCopyUnicodeString(&tempptr->wdn_RelativePath,pParentPath);
|
|
RtlAppendUnicodeStringToString(&tempptr->wdn_RelativePath,
|
|
&ubackslash);
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(&tempptr->wdn_RelativePath,pDirName);
|
|
tempptr->wdn_RelativePath.Buffer[tempptr->wdn_RelativePath.Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
tempptr->wdn_RelativePath.Buffer = NULL;
|
|
}
|
|
|
|
// push it on the stack
|
|
tempptr->wdn_Next = *ppStacktop;
|
|
*ppStacktop = tempptr;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*** afpPopDirNode
|
|
*
|
|
* Pop the top DirNode off of the stack and free it
|
|
*
|
|
***/
|
|
LOCAL
|
|
VOID
|
|
afpPopDirNode(
|
|
IN OUT PWALKDIR_NODE *ppStackTop
|
|
)
|
|
{
|
|
PWALKDIR_NODE tempptr;
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT(*ppStackTop != NULL);
|
|
|
|
tempptr = (*ppStackTop)->wdn_Next;
|
|
AfpFreeMemory(*ppStackTop);
|
|
*ppStackTop = tempptr;
|
|
|
|
}
|
|
|
|
/*** AfpGetNextDirectoryInfo
|
|
*
|
|
* Given a buffer full of FILE_DIRECTORY_INFORMATION entries as returned
|
|
* from a directory enumerate, find the next structure in the buffer and
|
|
* return the name information out of it, and whether or not the item
|
|
* is a file or directory. Also update the ppInfoBuf to point to the next
|
|
* available entry to return for the next time this routine is called.
|
|
*
|
|
*/
|
|
NTSTATUS
|
|
AfpGetNextDirectoryInfo(
|
|
IN OUT PFILE_DIRECTORY_INFORMATION *ppInfoBuf,
|
|
OUT PWCHAR *pNodeName,
|
|
OUT PULONG pNodeNameLen,
|
|
OUT PBOOLEAN pIsDir
|
|
)
|
|
{
|
|
PFILE_DIRECTORY_INFORMATION tempdirinfo;
|
|
|
|
PAGED_CODE( );
|
|
|
|
if (*ppInfoBuf == NULL)
|
|
{
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
tempdirinfo = *ppInfoBuf;
|
|
if (tempdirinfo->NextEntryOffset == 0)
|
|
{
|
|
*ppInfoBuf = NULL;
|
|
}
|
|
else
|
|
{
|
|
(PBYTE)*ppInfoBuf += tempdirinfo->NextEntryOffset;
|
|
}
|
|
|
|
*pIsDir = (tempdirinfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
|
|
True : False;
|
|
*pNodeNameLen = tempdirinfo->FileNameLength;
|
|
*pNodeName = tempdirinfo->FileName;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*** afpNwtDeleteFileEntity
|
|
*
|
|
* Delete a file or directory opening it with the name relative to phRelative
|
|
* handle.
|
|
* NOTE: can we use NtDeleteFile here since we dont really care about
|
|
* any security checking? Then we wouldn't even have to open a handle,
|
|
* although that routine opens one for DELETE_ON_CLOSE for us, then
|
|
* closes it.
|
|
*/
|
|
LOCAL
|
|
NTSTATUS
|
|
afpNwtDeleteFileEntity(
|
|
IN PFILESYSHANDLE phRelative,
|
|
IN PWCHAR Name,
|
|
IN ULONG Namelen,
|
|
IN BOOLEAN IsDir
|
|
)
|
|
{
|
|
ULONG OpenOptions;
|
|
FILESYSHANDLE hEntity;
|
|
NTSTATUS rc;
|
|
UNICODE_STRING uname;
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
|
|
("\nafpNwtDeleteFileEntity entered\n"));
|
|
|
|
OpenOptions = IsDir ? FILEIO_OPEN_DIR : FILEIO_OPEN_FILE;
|
|
|
|
AfpInitUnicodeStringWithNonNullTerm(&uname,(USHORT)Namelen,Name);
|
|
rc = AfpIoOpen(phRelative,
|
|
AFP_STREAM_DATA,
|
|
OpenOptions,
|
|
&uname,
|
|
FILEIO_ACCESS_DELETE,
|
|
FILEIO_DENY_ALL,
|
|
False,
|
|
&hEntity);
|
|
|
|
if (!NT_SUCCESS(rc))
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
rc = AfpIoMarkFileForDelete(&hEntity, NULL, NULL, NULL);
|
|
|
|
if (!NT_SUCCESS(rc))
|
|
{
|
|
// If the file is marked readonly, try clearing the RO attribute
|
|
if (((rc == STATUS_ACCESS_DENIED) || (rc == STATUS_CANNOT_DELETE)) &&
|
|
(NT_SUCCESS(AfpIoSetTimesnAttr(&hEntity,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
FILE_ATTRIBUTE_READONLY,
|
|
NULL,
|
|
NULL))))
|
|
|
|
{
|
|
rc = AfpIoMarkFileForDelete(&hEntity, NULL, NULL, NULL);
|
|
}
|
|
if (!NT_SUCCESS(rc))
|
|
{
|
|
DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
|
|
("\nafpNwtDeleteFileEntity: could not delete file/dir (rc=0x%lx)\n",rc));
|
|
DBGBRK(DBG_LEVEL_ERR);
|
|
}
|
|
}
|
|
// NOTE: if marking it for delete fails, at least we could try deleting
|
|
// the afpId stream so that we wouldn't find it at some future point...
|
|
|
|
AfpIoClose(&hEntity);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|