reciclaje - eliminar archivos temporales de cpanel
Eliminar un archivo basado en la identificación del disco (4)
¿Has examinado FILE_FLAG_POSIX_SEMANTICS? Le permitirá abrir archivos que difieren solo en caso de usar CreateFile .
Editar: Supongo que debería haber leído tu código primero porque veo que estás usando dicha bandera.
Como se describe aquí , el uso de SetFileInformationByHandle
con FILE_DISPOSITION_INFO
permite establecer un archivo con un código abierto que se eliminará cuando se cierren todos los identificadores.
Sin embargo, estoy tratando de eliminar un archivo basado en su índice de archivo (ID de disco) recuperado por FILE_DISPOSITION_INFO
y OpenFileById
para eliminar de manera segura los archivos / directorios en un directorio que difieren solo en el caso. Esto es seguro de hacer en mi caso de uso, ya que en un sistema NTFS, los índices de archivo son persistentes hasta su eliminación , negando el uso de ReplaceFile
, que maneja la base de código actual.
Sin embargo, cuando intento eliminar el identificador, obtengo el error 87 ( ERROR_INVALID_PARAMETER
). Si elimino utilizando un identificador creado con CreateFileW
, no encuentro ningún problema. No puedo hacer esto, sin embargo, ya que Windows no podrá distinguir entre dos archivos / carpetas del mismo caso, aunque NTFS sí.
También soy consciente de que existe una ambigüedad con los archivos con OpenFileById
abiertos con OpenFileById
, ya que los archivos con OpenFileById
comparten el mismo ID de disco. El problema de los archivos enlazados se puede considerar irrelevante para este escenario. Solo borraré directorios por ID, que no se puede vincular.
¿Hay algún parámetro o configuración que me falta en mi llamada a OpenFileById
? De alguna manera, en mi llamada SetFileInformationByHandle
?
Métodos adicionales que he probado:
- Llamar a
DuplicateHandle
con el manejadorOpenFileById
, proporcionandoDELETE
paradwDesiredAccess
y usando eso. Mismo resultadoERROR_INVALID_PARAMETER
. - Usando
ReOpenFile
con el manejadorOpenFileById
, proporcionandoDELETE
paradwDesiredAccess
, y usando eso. Mismo resultadoERROR_INVALID_PARAMETER
. - Utilizando
ReOpenFile
con el manejadorOpenFileById
, proporcionandoDELETE
paradwDesiredAccess
y proporcionando el indicadorFILE_FLAG_DELETE_ON_CLOSE
. No se proporciona ningún error, pero el archivo permanece después de que se cierran todos los identificadores.
Aquí hay un ejemplo mínimo, pero completo, que reproduce el problema:
#include <stdio.h>
#include <sys/stat.h>
#include <Windows.h>
DWORD getFileID(LPCWSTR path, LARGE_INTEGER *id)
{
HANDLE h = CreateFileW(path, 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT |
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_POSIX_SEMANTICS,
0);
if (h == INVALID_HANDLE_VALUE)
return GetLastError();
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(h, &info))
{
DWORD err = GetLastError();
CloseHandle(h);
return err;
}
id->HighPart = info.nFileIndexHigh;
id->LowPart = info.nFileIndexLow;
CloseHandle(h);
return ERROR_SUCCESS;
}
DWORD deleteFileHandle(HANDLE fileHandle)
{
FILE_DISPOSITION_INFO info;
info.DeleteFileW = TRUE;
if (!SetFileInformationByHandle(
fileHandle, FileDispositionInfo, &info, sizeof(info)))
{
return GetLastError();
}
return ERROR_SUCCESS;
}
int wmain(DWORD argc, LPWSTR argv[])
{
if (argc != 3)
{
fwprintf(stderr, L"Arguments: <rootpath> <path>/n");
return 1;
}
DWORD err;
HANDLE rootHandle = CreateFileW(
argv[1], 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT |
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_POSIX_SEMANTICS,
0);
if (rootHandle == INVALID_HANDLE_VALUE)
{
err = GetLastError();
fwprintf(stderr,
L"Could not open root directory ''%s'', error code %d/n",
argv[1], err);
return err;
}
LARGE_INTEGER fileID;
err = getFileID(argv[2], &fileID);
if (err != ERROR_SUCCESS)
{
fwprintf(stderr,
L"Could not get file ID of file/directory ''%s'', error code %d/n",
argv[2], err);
CloseHandle(rootHandle);
return err;
}
fwprintf(stdout,
L"The file ID of ''%s'' is %lld/n",
argv[2], fileID.QuadPart);
FILE_ID_DESCRIPTOR idStruct;
idStruct.Type = FileIdType;
idStruct.FileId = fileID;
HANDLE fileHandle = OpenFileById(
rootHandle, &idStruct, DELETE, FILE_SHARE_DELETE, 0,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
if (fileHandle == INVALID_HANDLE_VALUE)
{
err = GetLastError();
CloseHandle(rootHandle);
fwprintf(stderr,
L"Could not open file by ID %lld, error code %d/n",
fileID.QuadPart, err);
return err;
}
err = deleteFileHandle(fileHandle);
if (err != ERROR_SUCCESS)
{
fwprintf(stderr,
L"Could not delete file by ID ''%lld'', error code %d/n",
fileID.QuadPart, err);
}
CloseHandle(fileHandle);
struct _stat _tmp;
fwprintf(stdout,
L"File was %ssuccessfully deleted/n",
(_wstat(argv[2], &_tmp) == 0) ? L"not " : L"");
CloseHandle(rootHandle);
return err;
}
Cualquier solución debe funcionar con Vista y superior. Sugerencias para mejorar el código también son bienvenidas.
Hay una versión en modo de usuario del modo kernel ZwCreateFile
llamado NTCreteFile
que, entre otras cosas, le dará todos los derechos de acceso que no puede obtener con OpenFileById
(pero puede obtenerlo con CreateFile
). Puede hacer todo lo que CreateFile
puede hacer y más. Por ejemplo, incluso puede crear directorios.
La parte buena es que hay una forma POBJECT_ATTRIBUTES
(pero entretenida) de especificar un ID de archivo en el argumento POBJECT_ATTRIBUTES
también, para que obtengas el mejor de todos los mundos ... excepto que es una API aún más incómoda de llamar que tu ejecución API torpes de Windows.
Hay dos versiones de la documentación. Uno en:
https://msdn.microsoft.com/en-us/library/bb432380(v=vs.85).aspx
y uno en:
https://msdn.microsoft.com/en-us/library/windows/hardware/ff556465(v=vs.85).aspx
... que enlaza a la documentación de ZwCreateFile en:
https://msdn.microsoft.com/en-us/library/windows/hardware/ff566424(v=vs.85).aspx
La razón por la que señalo esto es que el primer artículo omite algunas de las cosas buenas (como abrir archivos por ID) que están documentadas en el último artículo. He encontrado que esto es común y también he descubierto que la mayoría de la funcionalidad Zw xxx documentada realmente existe en las funciones NT xxx equivalentes pero documentadas de forma incompleta. Así que tienes que mantener la boca perfecta para obtener la funcionalidad requerida.
Para hacer que FILE_DISPOSITION_INFO
funcione, debe especificar el acceso DELETE en la función CreateFile
como se informa en https://msdn.microsoft.com/en-us/library/windows/desktop/aa365539(v=VS.85).aspx :
Debe especificar indicadores de acceso apropiados al crear el identificador de archivo para usar con SetFileInformationByHandle. Por ejemplo, si la aplicación está utilizando FILE_DISPOSITION_INFO con el miembro DeleteFile establecido en TRUE, el archivo necesitaría DELETE el acceso solicitado en la llamada a la función CreateFile. Para ver un ejemplo de esto, consulte la sección Código de ejemplo. Para obtener más información acerca de los permisos de archivos, vea Seguridad de archivos y Derechos de acceso. Es decir
//...
HANDLE hFile = CreateFile( TEXT("tempfile"),
GENERIC_READ | GENERIC_WRITE | DELETE, //Specify DELETE access!
0 /* exclusive access */,
NULL,
CREATE_ALWAYS,
0,
NULL);
Pero parece que un identificador creado con OpenFileById()
no se puede usar porque la función no puede aceptar el indicador DELETE
.
Desde https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx en OpenFileById()
se puede leer: dwDesired
Acceso [en]
El acceso al objeto. El acceso se puede leer, escribir o ambos.
Incluso al configurar DELETE
o GENERIC_ALL
la función falla.
Si reemplaza el identificador pasado a SetFileInformationByHandle
con uno creado con la función CreateFile
que tiene el indicador DELETE
establecido, como se SetFileInformationByHandle
anteriormente, funciona.
Suponga que los archivos son XXX y xxx y desea eliminar XXX.
- MoveFile ("XXX", "Creo que es XXX")
- Si se cambió el nombre de XXX, DeleteFile ("Creo que es XXX")
- De lo contrario, DeleteFile ("XXX"); MoveFile ("Creo que es XXX", "xxx")
En cuanto a OpenFileById, como ha observado, existe una posible ambigüedad con un archivo con varios nombres (también conocidos como enlaces duros). Permitir el acceso DELETE podría causar estragos con esto, con un nombre inesperado que se eliminaría (si se dejara en el sistema de archivos para seleccionar cuál). Sospecho que optaron por el simple caso de no permitir el acceso a DELETE.
Un argumento similar podría hacerse para permitir enlaces duros a directorios. Claro, podrías hacerlo algunas veces correctamente, pero una vez que creaste un ciclo, las cosas se ponen mucho más difíciles ...