Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在win32 NTFS下检测文件移动?_C++_Visual Studio_Winapi_Visual C++_Ntfs - Fatal编程技术网

C++ 如何在win32 NTFS下检测文件移动?

C++ 如何在win32 NTFS下检测文件移动?,c++,visual-studio,winapi,visual-c++,ntfs,C++,Visual Studio,Winapi,Visual C++,Ntfs,我有一个数据库,可以将附加信息附加到文件系统(NTFS)中的任何文件。文件ID是它的完整路径,因此为了保持一致性,我需要观察数据库中的任何文件是否被删除、重命名或移动 目前,我正试图通过使用ReadDirectoryChangesW函数实现这一目标,该函数将FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME作为过滤条件 问题是这样,我只收到重命名、创建和删除的通知。因此,我需要根据“添加”和“删除”事件以及相关文件名猜测何时发生移

我有一个数据库,可以将附加信息附加到文件系统(NTFS)中的任何文件。文件ID是它的完整路径,因此为了保持一致性,我需要观察数据库中的任何文件是否被删除、重命名或移动

目前,我正试图通过使用ReadDirectoryChangesW函数实现这一目标,该函数将FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME作为过滤条件

问题是这样,我只收到重命名、创建和删除的通知。因此,我需要根据“添加”和“删除”事件以及相关文件名猜测何时发生移动(在同一卷上,移动[ctrl-x,ctrl-v]实际上是在文件创建之后立即删除文件,路径不同,但文件名保持不变)

有人知道有没有更好的解决办法吗

以下是我根据观察得出的理解:

关于在NTFS下移动文件 在同一卷中

对于相同文件名和不同路径(无论移动文件的大小),在“已删除”事件之后(没有延迟或延迟很小),会立即出现“已添加”事件

监视整个卷时的特殊情况:删除文件时,它实际上会添加到回收站(路径包含卷回收站,但文件名不同[某种哈希])

在两个不同的卷之间

首先,目标卷上有一个“已添加”事件。
之后,复制完成后,原始卷上会发生“已删除”事件


(注意:同时可能会发生几个事件:文件越大,延迟时间越长。)

如果这些文件在您的控制下(如果您在添加到数据库时具有写访问权限,然后始终至少具有读访问权限),我会在替代数据流中通过GUID或使用对象ID()标记它们,任何更合适的方法。

确实无法通知“移动”事件,因为此类事件始终是路径/文件名重命名或duo事件删除和创建的结果。
虽然使用USN日志可以使事情变得简单一点,但仍有一些额外的工作要做

在这种情况下,我需要动态检查文件系统的更改(我的应用程序在后台运行),因此没有必要使用日志(日志)

下面是我提出的用于DeviceIoControl和ReadDirectoryChangesW函数以及包含自定义FileActionInfo项的队列的逻辑

struct FileActionInfo {
    WCHAR fileName[FILE_NAME_MAX];
    CHAR drive;
    DWORD action;
    time_t timeStamp;
})

在使用USN时,这对于猜测所有移动事件可能也很有用:

算法 例外情况
  • 如果在不同卷上的同一会话期间创建了两个具有相同文件名的文件,并且之后删除了其中一个,则这将被错误地处理为“移动”事件
  • 随着时间的推移,FileActionInfo队列可能会变大,我们可以偶尔清理一下(设置移动文件的最大延迟)
    • 如果复制持续时间超过允许的延迟,我们可能会错过“移动”事件
    • 队列越大,将事件搜索到队列中的时间越长,因此我们需要快速访问:(使用不同访问模式存储相同项的几个对象:vector、hashtable)
为了以防万一,这是我写的FileActionQueue.h。
最重要的方法是Last()和Search(LPCWSTR文件名、DWORD操作、PCHAR驱动器)

#pragma一次
#包括
#包括
#包括
#包括
#包括
使用std::wstring;
使用std::vector;
使用std::map;
/*winnt.h中定义的常数:
#定义文件\u操作\u添加0x00000001
#定义文件\u操作\u已删除0x00000002
#定义文件\u操作\u修改0x00000003
#定义文件\u操作\u重命名\u旧\u名称0x00000004
#定义文件\u操作\u重命名\u新\u名称0x00000005
*/
#定义文件\u操作\u移动0x00000006
类FileActionInfo{
公众:
LPWSTR文件名;
字符驱动;
德沃德行动;
时间戳;
FileActionInfo(LPCWSTR文件名、字符驱动器、DWORD操作){
这个->文件名=(WCHAR*)全局分配(GPTR,sizeof(WCHAR)*(wcslen(文件名)+1));
wcscpy(此->文件名,文件名);
这个->驱动器=驱动器;
这个->动作=动作;
此->时间戳=时间(空);
}
~FileActionInfo(){
GlobalFree(此->文件名);
}
};
/*
有两种结构存储指向FileActionInfo项的指针:向量和映射。
这是因为我们需要能够:
1) 快速检索最新添加的项目
2) 在所有排队的项目中快速搜索(在这种情况下,我们使用文件名作为哈希代码)
*/
类FileActionQueue{
私人:
向量*qActionQueue;
地图*地图;
无效队列(vector*v,FileActionInfo*lpAction){
v->推回(lpAction);
}
无效出列(向量*v,文件操作信息*lpAction){
对于(int i=0,nCount=v->size();iat(i)){
v->erase(v->begin()+i);
打破
}
}
}
公众:
FileActionQueue(){
此->qActionQueue=新向量;
此->mActionMap=新地图;
}
~FileActionQueue(){
删除qActionQueue;
删除mActionMap;
}
无效添加(FileActionInfo*lpAction){
这个->队列(&(*this->mActionMap)[lpAction->fileName]),lpAction);
此->队列(此->qActionQueue,lpAction);
}
作废删除(FileActionInfo*lpAction){
this->Dequeue(&(*this->mActionMap)[lpAction->fileName]),lpAction);
this->Dequeue(this->qActionQueue,lpAction);
}
FileActionInfo*Last(){
vector*v=此->qActionQueue;
如果(v->size()==0)返回NULL;
返回v->at(v->size()-1);
}
FileActionInfo*搜索(LPCWSTR文件名、DWORD操作、PCHAR驱动器){
FileActionInfo*结果=NULL;
矢量
- when a 'added' event occurs
    - if previous event was a 'removed' event on same volume
        - if 'added' event contains recycle bin path, ignore it (file deleted)
        - else if 'removed' event contains recycle bin path, handle as a 'restored'/'undelete' event, remove 'removed' event from queue
        - else 
            - if 'added' event has same filename, handle as a 'moved' event, remove 'removed' event from queue
            - else push 'added' event to queue
    - else push 'added' event to queue


- when a 'removed' event occurs, search the queue for an 'added' event for the same filename on a different volume
    - if found, handle it as a 'moved' event and remove 'added' event from queue
    - else push 'removed' event to queue, launch a delayedRemoval thread


delayedRemoval thread(&FileActionInfo) {
    // we cannot wait forever , because 'added' event might never occur (if the file was actually deleted).
    sleep(2000)
    if given 'removed' event is still in the queue
        handle as an actual 'removed' event, and remove it from queue
    return;
}
#pragma once


#include <time.h>
#include <string>
#include <cwchar>
#include <vector>
#include <map>

using std::wstring;
using std::vector;
using std::map;

/* constants defined in winnt.h :
#define FILE_ACTION_ADDED                   0x00000001   
#define FILE_ACTION_REMOVED                 0x00000002   
#define FILE_ACTION_MODIFIED                0x00000003   
#define FILE_ACTION_RENAMED_OLD_NAME        0x00000004   
#define FILE_ACTION_RENAMED_NEW_NAME        0x00000005
*/
#define FILE_ACTION_MOVED                    0x00000006

class FileActionInfo {
public:
    LPWSTR    fileName;
    CHAR    drive;
    DWORD    action;
    time_t    timestamp;

    FileActionInfo(LPCWSTR fileName, CHAR drive, DWORD action) {        
        this->fileName = (WCHAR*) GlobalAlloc(GPTR, sizeof(WCHAR)*(wcslen(fileName)+1));
        wcscpy(this->fileName, fileName);
        this->drive = drive;
        this->action = action;
        this->timestamp = time(NULL);
    }

    ~FileActionInfo() {
        GlobalFree(this->fileName);    
    }
};

/*
There are two structures storing pointers to FileActionInfo items : a vector and a map. 
This is because we need to be able to:
1) quickly retrieve the latest added item
2) quickly search among all queued items (in which case we use fileName as hashcode)
*/
class FileActionQueue {
private:
    vector<FileActionInfo*> *qActionQueue;
    map<wstring, vector<FileActionInfo*>> *mActionMap;

    void Queue(vector<FileActionInfo*> *v, FileActionInfo* lpAction) {
        v->push_back(lpAction);
    }

    void Dequeue(vector<FileActionInfo*> *v, FileActionInfo* lpAction) {
        for(int i = 0, nCount = v->size(); i < nCount; ++i){
            if(lpAction == v->at(i)) {
                v->erase(v->begin() + i);
                break;
            }
        }
    }

public:

    FileActionQueue() {
        this->qActionQueue = new vector<FileActionInfo*>;
        this->mActionMap = new map<wstring, vector<FileActionInfo*>>;
    }

    ~FileActionQueue() {
        delete qActionQueue;
        delete mActionMap;    
    }

    void Add(FileActionInfo* lpAction) {
        this->Queue(&((*this->mActionMap)[lpAction->fileName]), lpAction);
        this->Queue(this->qActionQueue, lpAction);
    }

    void Remove(FileActionInfo* lpAction) {
        this->Dequeue(&((*this->mActionMap)[lpAction->fileName]), lpAction);
        this->Dequeue(this->qActionQueue, lpAction);
    }

    FileActionInfo* Last() {
        vector<FileActionInfo*> *v = this->qActionQueue;
        if(v->size() == 0) return NULL;
        return v->at(v->size()-1);
    }

    FileActionInfo* Search(LPCWSTR fileName, DWORD action, PCHAR drives) {
        FileActionInfo* result = NULL;
        vector<FileActionInfo*> *v;
        if( v = &((*this->mActionMap)[fileName])) {
            for(int i = 0, nCount = v->size(); i < nCount && !result; ++i){
                FileActionInfo* lpAction = v->at(i);
                if(wcscmp(lpAction->fileName, fileName) == 0 && lpAction->action == action) {
                    int j = 0;
                    while(drives[j] && !result) {
                        if(lpAction->drive == drives[j]) result = lpAction;
                        ++j;
                    }            
                }
            }
        }
        return result;
    }
};