【问题标题】:Windows Explorer directory as bundleWindows 资源管理器目录作为捆绑包
【发布时间】:2008-12-12 18:45:29
【问题描述】:

我一直在研究一种方法来防止我的用户意外进入我的应用程序的数据目录。

我的应用程序使用文件夹来存储结构化项目。文件夹内部结构是批评性的,不应该搞砸。我希望我的用户能看到整个文件夹,但无法打开它(就像 Mac 包一样)。

有没有办法在 Windows 上做到这一点?

从当前答案编辑

当然,我并不是要阻止我的用户访问他们的数据,只是防止他们意外破坏数据完整性。因此不需要加密或密码保护。

感谢大家对 .Net 的回答,但不幸的是,这主要是一个 C++ 项目,对 .Net 框架没有任何依赖。

我提到的数据不是光,它们是从电子显微镜获取的图像。这些数据可能很大(~100 MiB 到 ~1 GiB),因此不能将所有内容加载到内存中。这些是巨大的图像,因此存储必须提供一种通过一次访问一个文件而不将整个存档加载到内存中来增量读取数据的方法。

此外,该应用程序主要是遗留的,其中包含一些我们甚至不负责的组件。让我保留当前 IO 代码的解决方案更可取。

Shell Extension 看起来很有趣,我会进一步研究解决方案。

LarryF,你能详细说明一下 Filter Driver 或 DefineDOSDevice 吗?我不熟悉这些概念。

【问题讨论】:

  • 您能说明一下您所处的语言/环境吗?
  • 当然,更新了答案

标签: c++ windows directory bundle windows-shell


【解决方案1】:

您可以做几件事:

有一件事是您可以创建一个 FolderView Windows Shell 扩展,该扩展将为您的关键文件夹创建自定义视图。通过创建一个自定义的 FolderView,您可以使用一行文本“Nothing to see here”将文件夹设置为空白,或者您可以执行更复杂的操作,例如使用相同方法的 GAC 查看器。这种方法会相当复杂,但可以通过使用类似this CodeProject 文章库之类的东西作为基础来减轻这种复杂性。

另一种解决方案是使用 ZIP 虚拟文件系统,这需要您替换任何直接使用 System.IO 的代码来使用其他东西。 ASP.NET 2.0 正是出于这个原因,您可以在此基础上轻松构建,看看这个MSDN Article on implementation a VirtualPathProvider。

【讨论】:

  • 看起来这主要是针对 ASP.NET 的,但他没有提到任何关于 ASP 或 .NET 的内容……但是想法很好。
【解决方案2】:

Structured Storage 专为您描述的场景而设计:

结构化存储通过将单个文件作为称为存储和流的对象的结构化集合处理,在 COM 中提供文件和数据持久性。

“存储”类似于文件夹,“流”类似于文件。基本上,您只有一个文件,当使用结构化存储 API 访问该文件时,它的行为和外观就像一个完整的、自包含的文件系统。

但请注意:

对 COM 技术的深刻理解是结构化存储开发使用的先决条件。

【讨论】:

  • 如果我没有那么多遗留代码,这将是一个好主意。我快速查看了 API,看起来您必须使用特定的 COM 接口 (IStream) 才能读取存储中“文件”的内容。所以没有办法在这个解决方案中使用现有的代码。
【解决方案3】:

在您的程序内部还是外部?

有很多方法,但没有一个是容易的。您可能会查看文件系统上的过滤器驱动程序。

【讨论】:

    【解决方案4】:

    如果您采用 ZIP 文件方法(我为您考虑过,但没有提及),我建议您使用 deflate 算法,但使用您自己的文件系统...查看类似 TAR 格式的内容。然后,只需编写代码以在将所有 I/O 写入磁盘时通过 Inflate/Deflate 算法。我不会使用 ZIP“FORMAT”,因为它太容易查看文件,找到 PK 作为前两个字节,然后解压缩文件....

    我最喜欢 Joshperry 的建议。

    当然,您也可以编写将所有数据存储在单个文件中的设备驱动程序,但同样,我们正在研究驱动程序。 (我不确定您是否可以在驱动程序之外实现它。您可能可以,并且在您的程序内部调用 DefineDOSDevice,给它一个只有您的代码可以访问的名称,它将被视为一个普通的文件系统。 )。我会尝试一些想法,如果它们有效,我会给你一个样本。现在你让我感兴趣了。

    【讨论】:

      【解决方案5】:

      您可以将您的项目目录包装成一个 .zip 文件并将您的数据存储在那里,就像使用 .jar 一样(我知道 .jar 几乎是只读的,这是为了示例)。做一个非标准的扩展,这样双击不会立即生效,完成。 ;-)

      当然,这意味着您必须包装所有文件 IO 才能使用 .zip,这取决于您的程序是如何构建的,这可能很乏味。它已经为 Java 完成了:TrueZip。也许你可以以此为灵感?

      如果您受到诱惑 - 我不建议您摆弄文件夹权限,原因很明显,这无济于事。

      【讨论】:

        【解决方案6】:

        您可以使用独立存储。

        http://www.ondotnet.com/pub/a/dotnet/2003/04/21/isolatedstorage.html

        它并不能解决所有问题,但它确实使应用数据远离危险。

        【讨论】:

          【解决方案7】:

          请记住:如果您将其存储在文件系统中,用户将始终能够看到它。篡改资源管理器,我改用 cmd.exe。或总指挥官。或者别的什么。

          如果你不希望别人弄乱你的文件,我建议

          • 加密它们以防止文件被篡改
          • 将它们放入存档(即 ZIP)中,可能对其进行密码保护,然后在运行时压缩/解压缩(我会查找能够快速修改存档的算法)

          这当然不是完全保护,但它的实施相当简单,不需要您在操作系统中安装时髦的东西,并且应该让大多数好奇的用户远离。

          当然,如果不控制计算机本身,您将永远无法完全控制用户计算机上的文件。

          【讨论】:

            【解决方案8】:

            我见过使用 Tomalak 建议的 zip 存档作为“项目文件”的软件(Visual Paradigm 的 Agilian)。 Zip 文件很好理解,使用非标准文件扩展名确实可以防止临时用户弄乱“文件”。这样做的一大优势是,如果发生损坏,可以使用标准工具来解决问题,您不必担心创建特殊工具来支持您的主应用程序。

            【讨论】:

              【解决方案9】:

              看起来一些Windows ports of FUSE 开始出现了。我认为这将是最好的解决方案,因为它可以让我保持遗留代码(相当大)不变。

              【讨论】:

                【解决方案10】:

                我很高兴听到您在 C++ 中执行此操作。似乎没有人认为 C++ 是“必要的”了。这全是 C#,还有 ASP.NET……即使我在全 C# 的房子里工作,当我发誓我会永远切换,因为 C++ 做了我需要做的所有事情,然后一些。我已经成年了,可以清理自己的记忆了!呵呵..不管怎样,回到手头的问题...

                DefineDOSDevice() 是一种用于分配驱动器号、端口名称(LPT1、COM1 等)的方法。你给它一个名字,一些标志和一个处理这个设备的“路径”。但是,不要让它愚弄你。它不是文件系统路径,而是 NT 对象路径。我确定您已经将它们视为“\Device\HardDisk0”等。您可以使用 sysinternals 中的 WinObj.exe 来了解我的意思。无论如何,您可以创建一个设备驱动程序,然后将一个 MSDOS 符号链接指向它,然后您就可以关闭并运行了。但是,对于最初的问题,这似乎需要做很多工作。

                典型目录中有多少这些兆到千兆字节的文件?您最好将所有文件粘贴在一个巨型文件中,并在其旁边存储一个索引文件(或每个文件的标题),该文件指向“虚拟文件系统”文件中的下一个“文件”。

                一个很好的例子可能是查看 Microsoft MSN 存档格式。当我在一家 AV 公司工作时,我颠倒了这种存档格式,它实际上非常有创意,但非常简单。可以在一个文件中完成所有操作,如果您想要花哨,可以将数据存储在 RAID 5 类型配置中的 3 个文件中,因此,如果 3 个文件中的任何一个文件被占用,你可以重建其他的。此外,用户只会在一个目录中看到 3 个 非常 个大文件,并且无法访问单个(内部)文件。

                我为您提供了解压缩这些 MSN 存档格式之一的代码。我没有创建一个的代码,但是从提取源中,您可以毫无问题地构建/编写一个。如果文件经常被删除和/或重命名,则可能会导致文件中的已用空间出现问题,需要不时进行修剪。

                这种格式甚至支持 CRC 字段,因此您可以测试文件是否正常。我永远无法完全逆转 Microsoft 用于对数据进行 CRC 校验的算法,但我有一个非常好的主意。

                您将无法保留当前的 ​​I/O 例程,这意味着 CreateFile() 不仅能够打开存档中的任何文件,但是,凭借 C++ 的超酷特性,您可以覆盖 CreateFile调用以实现您的存档格式。

                如果你需要他的帮助,而且这是一个足够大的问题,也许我们可以离线讨论并为你找到解决方案。

                我不反对给你写一个 FileSystemDriver,但为此,我们必须开始谈论补偿。就像我现在所做的那样,我非常乐意免费为您提供指导和想法。

                我不确定我在这里给你我的电子邮件地址是否符合犹太教规,我不确定 SO 对此的政策,因为我们可能会谈论潜在的工作/招揽,但这不是我唯一的意图.我宁愿先帮你找到自己的解决方案。

                在查看设备驱动程序之前,请下载 WinDDK。它到处都是驱动程序示例。

                如果你想知道我为什么这么关心这个,那是因为我多年来一直在写一个与此类似的驱动程序,它必须与 Windows OSX兼容,这将允许用户在不安装任何驱动程序或复杂(而且体积庞大,有时令人讨厌)的软件的情况下保护驱动器卷(USB 密钥、可移动卷)。近年来,很多硬件制造商都在做类似的事情,但我认为安全性并不是那么安全。我正在研究使用 RSA 和 AES,与 GPG 和 PGP 的工作方式完全相同。最初,我被告知(我相信,但没有证据)将用于保护 MP3 文件的内容。由于它们将以加密格式存储,因此如果没有正确的密码短语,它们根本无法工作。但是,我也看到了它的其他用途。 (当一个 16 兆(是 MEG)USB 密钥的成本超过 100 美元左右时,这又回来了)。

                该项目还与我的石油和天然气行业 PC 安全系统一起使用,该系统使用类似于智能卡的东西,只是更容易使用、重复使用/重新发行、不可能(阅读:非常困难和不太可能) hack,我可以在家里自己的孩子身上使用它! (因为总是争论谁在电脑上的时间,谁得到的最多,等等,等等,等等......)

                唷.. 我想我在这里跑题了。无论如何,这里是 Microsoft MSN 存档格式的示例。看看你是否可以使用这样的东西,知道你总是可以通过在主文件中解析/搜索请求的文件时跟踪文件中的偏移量来“跳过”文件;或在内存中保存的预解析数据中。而且由于您不会在内存中加载原始二进制文件数据,因此您唯一的限制可能是 32 位机器上的 4gb 文件限制。

                MARC(Microsoft MSN 存档)格式的布局(松散)如下:

                • 12 字节头(只有一个)
                  • 文件魔术
                  • MARC 版本
                  • 文件数(见下表)
                • 68 字节文件表头(1 到 Header.NumFiles 这些)
                  • 文件名
                  • 文件大小
                  • 校验和
                  • 原始文件数据的偏移量

                现在,在 12 字节文件表条目中,32 位用于文件长度和偏移量。对于非常大的文件,您可能必须将其增加到 48 或 64 位整数。

                这是我编写的一些代码来处理这些问题。

                #define MARC_FILE_MAGIC         0x4352414D // In Little Endian
                #define MARC_FILENAME_LEN       56 //(You'll notice this is rather small)
                #define MARC_HEADER_SIZE        12
                #define MARC_FILE_ENT_SIZE      68
                
                #define MARC_DATA_SIZE          1024 * 128 // 128k Read Buffer should be enough.
                
                #define MARC_ERR_OK              0      // No error
                #define MARC_ERR_OOD             314    // Out of data error
                #define MARC_ERR_OS              315    // Error returned by the OS
                #define MARC_ERR_CRC             316    // CRC error
                
                struct marc_file_hdr
                {
                    ULONG            h_magic;
                    ULONG            h_version;
                    ULONG            h_files;
                    int              h_fd;
                    struct marc_dir *h_dir;
                };
                
                struct marc_file
                {
                    char            f_filename[MARC_FILENAME_LEN];
                    long            f_filesize;
                    unsigned long   f_checksum;
                    long            f_offset;
                };
                
                struct marc_dir
                {
                    struct marc_file       *dir_file;
                    ULONG                   dir_filenum;
                    struct marc_dir        *dir_next;
                };
                

                这让您了解我为它们编写的标题,这里是 open 函数。是的,它缺少所有的支持电话、错误例程等,但你明白了。请原谅 C 和 C++ 代码风格的混合。我们的扫描仪是由许多类似这样的不同问题组成的集群……我使用了诸如 open()、fopen() 之类的古老调用来与代码库的其余部分保持标准。

                struct marc_file_hdr *marc_open(char *filename)
                {
                    struct marc_file_hdr *fhdr  = (struct marc_file_hdr*)malloc(sizeof(marc_file_hdr));
                    fhdr->h_dir = NULL;
                
                #if defined(_sopen_s)
                    int errno = _sopen_s(fhdr->h_fd, filename, _O_BINARY | _O_RDONLY, _SH_DENYWR, _S_IREAD | _S_IWRITE);
                #else
                    fhdr->h_fd = open(filename, _O_BINARY | _O_RDONLY);
                #endif
                    if(fhdr->h_fd < 0)
                    {
                        marc_close(fhdr);
                        return NULL;
                    }
                
                    //Once we have the file open, read all the file headers, and populate our main headers linked list.
                    if(read(fhdr->h_fd, fhdr, MARC_HEADER_SIZE) != MARC_HEADER_SIZE)
                    {
                        errmsg("MARC: Could not read MARC header from file %s.\n", filename);
                        marc_close(fhdr);
                        return NULL;
                    }
                
                    // Verify the file magic
                    if(fhdr->h_magic != MARC_FILE_MAGIC)
                    {
                        errmsg("MARC: Incorrect file magic %x found in MARC file.", fhdr->h_magic);
                        marc_close(fhdr);
                        return NULL;
                    }
                
                    if(fhdr->h_files <= 0)
                    {
                        errmsg("MARC: No files found in archive.\n");
                        marc_close(fhdr);
                        return NULL;
                    }
                
                    // Get all the file headers from this archive, and link them to the main header.
                    struct marc_dir *lastdir = NULL, *curdir = NULL;
                    curdir = (struct marc_dir*)malloc(sizeof(marc_dir));
                    fhdr->h_dir = curdir;
                
                    for(int x = 0;x < fhdr->h_files;x++)
                    {
                        if(lastdir)
                        {
                            lastdir->dir_next = (struct marc_dir*)malloc(sizeof(marc_dir));
                            lastdir->dir_next->dir_next = NULL;
                            curdir = lastdir->dir_next;
                        }
                
                        curdir->dir_file = (struct marc_file*)malloc(sizeof(marc_file));
                        curdir->dir_filenum = x + 1;
                
                        if(read(fhdr->h_fd, curdir->dir_file, MARC_FILE_ENT_SIZE) != MARC_FILE_ENT_SIZE)
                        {
                            errmsg("MARC: Could not read file header for file %d\n", x);
                            marc_close(fhdr);
                            return NULL;
                        }
                        // LEF: Just a little extra insurance...
                        curdir->dir_file->f_filename[MARC_FILENAME_LEN] = NULL;
                
                        lastdir = curdir;
                    }
                    lastdir->dir_next = NULL;
                
                    return fhdr;
                }
                

                然后,您就有了简单的提取方法。请记住,这仅用于病毒扫描,因此没有搜索例程等。这旨在简单地转储文件,对其进行扫描,然后继续。下面是我相信 Microsoft 使用的 CRC 代码例程,但我不确定他们 CRC 究竟是什么什么。它可能包括标题数据+文件数据等。我只是没有足够的心思回去尝试扭转它。无论如何,如您所见,这种存档格式没有压缩,但它很容易添加。非常。如果您愿意,可以提供完整的源代码。 (我认为剩下的就是 close() 例程,以及调用和提取每个文件的代码等!!)

                bool marc_extract(struct marc_file_hdr *marc, struct marc_file *marcfile, char *file, int &err)
                {
                    // Create the file from marcfile, in *file's location, return any errors.
                    int ofd = 0;
                #if defined(_sopen_s)
                     err = _sopen_s(ofd, filename, _O_CREAT | _O_SHORT_LIVED | _O_BINARY | _O_RDWR, _SH_DENYNO, _S_IREAD | _S_IWRITE);
                #else
                    ofd = open(file, _O_CREAT | _O_SHORT_LIVED | _O_BINARY | _O_RDWR);
                #endif
                
                    // Seek to the offset of the file to extract
                    if(lseek(marc->h_fd, marcfile->f_offset, SEEK_SET) != marcfile->f_offset)
                    {
                        errmsg("MARC: Could not seek to offset 0x%04x for file %s.\n", marcfile->f_offset, marcfile->f_filename);
                        close(ofd);
                        err = MARC_ERR_OS; // Get the last error from the OS.
                        return false;
                    }
                
                    unsigned char *buffer = (unsigned char*)malloc(MARC_DATA_SIZE);
                
                    long bytesleft = marcfile->f_filesize;
                    long readsize = MARC_DATA_SIZE >= marcfile->f_filesize ? marcfile->f_filesize : MARC_DATA_SIZE;
                    unsigned long crc = 0;
                
                    while(bytesleft)
                    {
                        if(read(marc->h_fd, buffer, readsize) != readsize)
                        {
                            errmsg("MARC: Failed to extract data from MARC archive.\n");
                            free(buffer);
                            close(ofd);
                            err = MARC_ERR_OOD;
                            return false;
                        }
                
                        crc = marc_checksum(buffer, readsize, crc);
                
                        if(write(ofd, buffer, readsize) != readsize)
                        {
                            errmsg("MARC: Failed to write data to file.\n");
                            free(buffer);
                            close(ofd);
                            err = MARC_ERR_OS; // Get the last error from the OS.
                            return false;
                        }
                        bytesleft -= readsize;
                        readsize = MARC_DATA_SIZE >= bytesleft ? bytesleft : MARC_DATA_SIZE;
                    }
                
                    // LEF:  I can't quite figure out how the checksum is computed, but I think it has to do with the file header, PLUS the data in the file, or it's checked on the data in the file
                    //       minus any BOM's at the start...  So, we'll just rem out this code for now, but I wrote it anyways.
                    //if(crc != marcfile->f_checksum)
                    //{
                    //    warningmsg("MARC: File CRC does not match.  File could be corrupt, or altered.  CRC=0x%08X, expected 0x%08X\n", crc, marcfile->f_checksum);
                    //    err = MARC_ERR_CRC;
                    //}
                
                    free(buffer);
                    close(ofd);
                
                    return true;
                }
                

                这是我假设的 CRC 例程(我可能是从 Stuart Caie 和 libmspack 那里偷来的,我不记得了):

                static unsigned long marc_checksum(void *pv, UINT cb, unsigned long seed)
                {
                    int count = cb / 4;
                    unsigned long csum = seed;
                    BYTE *p = (BYTE*)pv;
                    unsigned long ul;
                
                    while(count-- > 0)
                    {
                        ul = *p++;
                        ul |= (((unsigned long)(*p++)) <<  8);
                        ul |= (((unsigned long)(*p++)) << 16);
                        ul |= (((unsigned long)(*p++)) << 24);
                        csum ^= ul;
                    }
                
                    ul = 0;
                    switch(cb % 4)
                    {
                        case 3: ul |= (((unsigned long)(*p++)) << 16);
                        case 2: ul |= (((unsigned long)(*p++)) <<  8);
                        case 1: ul |= *p++;
                        default: break;
                    }
                    csum ^= ul;
                
                    return csum;
                }                                                                                     
                

                好吧,我认为这篇文章现在已经够长了...如果您需要帮助或有任何问题,请联系我。

                【讨论】:

                  猜你喜欢
                  • 2011-07-29
                  • 1970-01-01
                  • 2010-12-09
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-12-09
                  • 1970-01-01
                  • 2012-05-03
                  相关资源
                  最近更新 更多