A overlay fs based on mirror.c

12 views
Skip to first unread message

wang peikai

unread,
Apr 6, 2025, 3:44:58 AMApr 6
to Dokan
Hi Liryna!
This is my code about dokan's mirroc MirrorCreateFile function, mainly used to implement write operations into writePath, read operations are read writePath (upperPath) first, If you don't read lowerPath again, but this code encounters a problem, if you just click or enter the directory (no actual writing occurs), the corresponding directory will also be generated in upperPath, thus overlayfs will not work properly. Could you please help me to check that what's the problem with the code :



static NTSTATUS DOKAN_CALLBACK

MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext,

                 ACCESS_MASK DesiredAccess, ULONG FileAttributes,

                 ULONG ShareAccess, ULONG CreateDisposition,

                 ULONG CreateOptions, PDOKAN_FILE_INFO DokanFileInfo) {


  WCHAR filePath[DOKAN_MAX_PATH] = {0};

  WCHAR lowerFilePath[DOKAN_MAX_PATH] = {0};

  WCHAR writeFilePath[DOKAN_MAX_PATH] = {0};

  HANDLE handle;

  DWORD fileAttr;

  NTSTATUS status = STATUS_SUCCESS;

  DWORD creationDisposition;

  DWORD fileAttributesAndFlags;

  DWORD error = 0;

  SECURITY_ATTRIBUTES securityAttrib;

  ACCESS_MASK genericDesiredAccess;

  // userTokenHandle is for Impersonate Caller User Option

  HANDLE userTokenHandle = INVALID_HANDLE_VALUE;


  // 设置 SECURITY_ATTRIBUTES

  securityAttrib.nLength = sizeof(securityAttrib);

  securityAttrib.lpSecurityDescriptor =

      SecurityContext->AccessState.SecurityDescriptor;

  securityAttrib.bInheritHandle = FALSE;


  // 映射内核创建文件标志到用户空间

  DokanMapKernelToUserCreateFileFlags(

      DesiredAccess, FileAttributes, CreateOptions, CreateDisposition,

      &genericDesiredAccess, &fileAttributesAndFlags, &creationDisposition);


  GetLowerFilePath(lowerFilePath, DOKAN_MAX_PATH, FileName);

  GetFilePath(filePath, DOKAN_MAX_PATH, FileName);

  GetWriteFilePath(writeFilePath, DOKAN_MAX_PATH, FileName);

  BOOL isWrite = IsWriteOperation(DesiredAccess, CreateDisposition,

                                  FileOrPathExists(lowerFilePath));

  DWORD srcAttrs = GetFileAttributes(lowerFilePath);

  BOOL isLowerFileExist = (srcAttrs != INVALID_FILE_ATTRIBUTES) &&

                          !(srcAttrs & FILE_ATTRIBUTE_DIRECTORY);


  if (!isWrite) {

    // 读操作:优先检查 tmpwrite 是否存在

    if (PathFileExists(writeFilePath)) {

      wcscpy_s(filePath, DOKAN_MAX_PATH, writeFilePath);

    }

  } else {

    EnsureParentDirExists(writeFilePath);

    if (isLowerFileExist && !FileOrPathExists(writeFilePath)) {

      CopyFileFromSource(filePath, writeFilePath);

    }

    // 写操作:强制使用 tmpwrite 路径

    wcscpy_s(filePath, DOKAN_MAX_PATH, writeFilePath);

  }


  // 当 filePath 指向的是目录时,调整标志以便正确打开

  fileAttr = GetFileAttributes(filePath);


  if (fileAttr != INVALID_FILE_ATTRIBUTES &&

      fileAttr & FILE_ATTRIBUTE_DIRECTORY) {

    if (CreateOptions & FILE_NON_DIRECTORY_FILE) {

      DbgPrint(L"\tCannot open a dir as a file\n");

      return STATUS_FILE_IS_A_DIRECTORY;

    }


    DokanFileInfo->IsDirectory = TRUE;

    // 需要通过 FindFirstFile 来列出目录中的文件

    ShareAccess |= FILE_SHARE_READ;

  }


  DbgPrint(L"\tFlagsAndAttributes = 0x%x\n", fileAttributesAndFlags);


  // 保留原有标志处理逻辑

  if (g_CaseSensitive)

    fileAttributesAndFlags |= FILE_FLAG_POSIX_SEMANTICS;


  // 处理用户身份验证(如果启用)

  if (g_ImpersonateCallerUser) {

    userTokenHandle = DokanOpenRequestorToken(DokanFileInfo);


    if (userTokenHandle == INVALID_HANDLE_VALUE) {

      DbgPrint(L"  DokanOpenRequestorToken failed\n");

      // 可以选择返回错误状态

    }

  }


  if (DokanFileInfo->IsDirectory) {

    if (wcsstr(filePath, L"www") != NULL) {

      wprintf(L"it is www\n");

    }

    // 处理创建目录请求

    if (creationDisposition == CREATE_NEW ||

        creationDisposition == OPEN_ALWAYS) {

      if (g_ImpersonateCallerUser && userTokenHandle != INVALID_HANDLE_VALUE) {

        // 如果启用了用户模拟,进行身份模拟

        if (!ImpersonateLoggedOnUser(userTokenHandle)) {

          // 处理身份模拟失败

          DbgPrint(L"\tImpersonateLoggedOnUser failed.\n");

        }

      }

      wprintf(L"create creationDisposition CREATE_NEW  %s to %s\n", filePath,

              writeFilePath);

      // We create folder

      if (!CreateDirectory(filePath, &securityAttrib)) {

        error = GetLastError();

        // Fail to create folder for OPEN_ALWAYS is not an error

        if (error != ERROR_ALREADY_EXISTS ||

            creationDisposition == CREATE_NEW) {

          DbgPrint(L"\terror code = %d\n\n", error);

          status = DokanNtStatusFromWin32(error);

        }

      }


      if (g_ImpersonateCallerUser && userTokenHandle != INVALID_HANDLE_VALUE) {

        // Clean Up operation for impersonate

        DWORD lastError = GetLastError();

        if (status != STATUS_SUCCESS) //Keep the handle open for CreateFile

          CloseHandle(userTokenHandle);

        RevertToSelf();

        SetLastError(lastError);

      }

    }


    if (status == STATUS_SUCCESS) {

      // 检查是否尝试将文件作为目录打开

      if (fileAttr != INVALID_FILE_ATTRIBUTES &&

          !(fileAttr & FILE_ATTRIBUTE_DIRECTORY) &&

          (CreateOptions & FILE_DIRECTORY_FILE)) {

        return STATUS_NOT_A_DIRECTORY;

      }


      if (g_ImpersonateCallerUser && userTokenHandle != INVALID_HANDLE_VALUE) {

        // 取消模拟用户身份

        if (!ImpersonateLoggedOnUser(userTokenHandle)) {

          DbgPrint(L"\tImpersonateLoggedOnUser failed.\n");

        }

      }


      // FILE_FLAG_BACKUP_SEMANTICS 对于打开目录句柄是必需的

      handle =

          CreateFile(filePath, genericDesiredAccess, ShareAccess,

                     &securityAttrib, OPEN_EXISTING,

                     fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, NULL);


      // 取消模拟用户身份

      if (g_ImpersonateCallerUser && userTokenHandle != INVALID_HANDLE_VALUE) {

        DWORD lastError = GetLastError();

        CloseHandle(userTokenHandle);

        RevertToSelf();

        SetLastError(lastError);

      }


      if (handle == INVALID_HANDLE_VALUE) {

        error = GetLastError();

        DbgPrint(L"\terror code = %d\n\n", error);


        status = DokanNtStatusFromWin32(error);

      } else {

        DokanFileInfo->Context = (ULONG64)handle; // 保存文件句柄到 Context


        // 如果是 OPEN_ALWAYS 且文件已存在,返回 STATUS_OBJECT_NAME_COLLISION

        if (creationDisposition == OPEN_ALWAYS &&

            fileAttr != INVALID_FILE_ATTRIBUTES)

          return STATUS_OBJECT_NAME_COLLISION;

      }

    }

  } else {

    // 处理创建文件请求

    // 防止未设置隐藏或系统属性的文件被覆盖

    if (fileAttr != INVALID_FILE_ATTRIBUTES &&

        ((!(fileAttributesAndFlags & FILE_ATTRIBUTE_HIDDEN) &&

          (fileAttr & FILE_ATTRIBUTE_HIDDEN)) ||

         (!(fileAttributesAndFlags & FILE_ATTRIBUTE_SYSTEM) &&

          (fileAttr & FILE_ATTRIBUTE_SYSTEM))) &&

        (creationDisposition == TRUNCATE_EXISTING ||

         creationDisposition == CREATE_ALWAYS))

      return STATUS_ACCESS_DENIED;

    // 防止删除只读文件

    if ((fileAttr != INVALID_FILE_ATTRIBUTES &&

             (fileAttr & FILE_ATTRIBUTE_READONLY) ||

         (fileAttributesAndFlags & FILE_ATTRIBUTE_READONLY)) &&

        (fileAttributesAndFlags & FILE_FLAG_DELETE_ON_CLOSE))

      return STATUS_CANNOT_DELETE;

    // 截断操作必须具有写访问权限

    if (creationDisposition == TRUNCATE_EXISTING)

      genericDesiredAccess |= GENERIC_WRITE;


    if (g_ImpersonateCallerUser && userTokenHandle != INVALID_HANDLE_VALUE) {

      // 如果启用了用户模拟,进行身份模拟

      if (!ImpersonateLoggedOnUser(userTokenHandle)) {

        // 处理身份模拟失败

        DbgPrint(L"\tImpersonateLoggedOnUser failed.\n");

      }

    }


    // 打开文件句柄

    handle =

        CreateFile(filePath, genericDesiredAccess, ShareAccess, &securityAttrib,

                   creationDisposition, fileAttributesAndFlags, NULL);


    if (g_ImpersonateCallerUser && userTokenHandle != INVALID_HANDLE_VALUE) {

      // 取消模拟用户身份

      DWORD lastError = GetLastError();

      CloseHandle(userTokenHandle);

      RevertToSelf();

      SetLastError(lastError);

    }

    if (handle == INVALID_HANDLE_VALUE) {

      error = GetLastError();

      DbgPrint(L"\terror code = %d\n\n", error);

      wprintf(L"got a errrrrrr file:%s code = %d\n", filePath, error);

      status = DokanNtStatusFromWin32(error);

    } else {

      // 如果是 TRUNCATE_EXISTING,更新文件属性

      if (fileAttr != INVALID_FILE_ATTRIBUTES &&

          creationDisposition == TRUNCATE_EXISTING) {

        SetFileAttributes(filePath, fileAttributesAndFlags | fileAttr);

      }


      DokanFileInfo->Context = (ULONG64)handle; // 保存文件句柄到 Context

      // 如果是 OPEN_ALWAYS 或 CREATE_ALWAYS 且文件已存在,返回 STATUS_OBJECT_NAME_COLLISION

      if (creationDisposition == OPEN_ALWAYS ||

          creationDisposition == CREATE_ALWAYS) {

        error = GetLastError();

        if (error == ERROR_ALREADY_EXISTS) {

          DbgPrint(L"\tOpen an already existing file\n");

          status = STATUS_OBJECT_NAME_COLLISION;

        }

      }

    }

  }

  DbgPrint(L"\n");

  return status;

}



 
Reply all
Reply to author
Forward
0 new messages