您的位置:首页 > 其它

【转帖】用Win32 API设置windows XP中FAT32文件夹共享的读写权限

2008-11-06 15:41 441 查看
刚加入博客园。虽然以前也经常在园子里晃悠,但只记得欣赏大牛们的大作了,从来没灌过水。这算是我的处女贴,权当是给园子增加流量了。
毕业走出校门,找了份编程的差事。

刚一上岗,就接到项目组长分派的任务:用Win32 API编程实现Windows XP中FAT32格式的文件夹只读共享,为啥要强调是FAT32格式呢?原来在我来之前,现在这个项目的老版本里已经实现了NTFS格式的文件共享,现在是应客户的要求添加对FAT32格式的支持。

估计是项目组长认为这个问题实在是太简单了,已经有了现成的葫芦,照个画个瓢就行了,这种低级的任务也只有我这个新来的菜鸟值得花功夫去弄一下。

我刚开始也认为这个任务应该不难,虽然我绝对是个Win32 API编程的菜鸟,以前从来没用接触过这东西。一方面是在我以前的编程任务中用不着,另外也是没胆量碰这东西,光是一堆字符串转换就把我给晕死了。

好了,废话少说,开始谈正事。

首先说下Windows操作系统下NTFS和FAT32文件格式在文件共享,读写权限设置方面的区别,简单点说就是:

NTFS文件系统自身可以设置文件和文件夹的读写访问权限,而FAT32文件系统则不具备这样的功能;
NTFS文件系统的读写访问控制对于本地用户访问以及通过文件夹共享的网络访问均有效;
文件夹共享也有自己的访问权限设置,但这种访问控制机制仅对网络访问有效,对本地用户访问不起作用;
如果共享的文件夹在NTFS分区,则网络访问同时受到共享权限设置和NTFS文件系统权限设置的控制,最终的约束结果为两者中较严格的那一项约束。

更具体的信息请参考:http://technet2.microsoft.com/windowsserver/en/library/ee26f971-c9d4-444f-9622-d5d756229e5e1033.mspx?mfr=true

为了更直观的表达这些意思,可以看下下面的示意图:

首先,取消掉Windows XP默认的“使用简单文件夹共享”,再分别查看FAT32分区和NTFS分区中文件夹的“属性”对话框:



左侧的对话框为FAT32格式的,右侧的为NTFS格式的,可见后者比前者多了一项:安全

打开两个对话框中都有的“权限”,再打开NTFS属性对话框中的“安全”:



这就是我们通过Windows UI设置访问权限的地方了,左边的权限仅对网络共享控制器作用,右边的属于NTFS特有的访问控制功能,对于本地用户访问和网络访问都有效。

以上演示的是通过windows UI设置权限的方法,另外,对于我们这些吃编程饭的人来说,还有必要知道有没有相应的DOS命令来实现同样的功能。对于左侧的设置网络访问权限的功能,我还没有找出相应的命令(要是很容易找到这样的命令,我就不在这里灌这桶水了:));对于右侧的设置NTFS访问权限的功能,有一个专用的DOS命令可以使用:cacls,用法请参考DOS帮助或参考MS的资料:http://support.microsoft.com/kb/318754/

说了这么多,貌似还是没有切入正题,我想说的是该怎么编程啊,暴汗~~

先说下如何用API编程实现NTFS的文件夹共享:

调用API函数NetShareAdd()将文件夹设置为共享,调用此函数后如果不做其他设置,网络用户是无法访问共享文件夹的,因为此文件夹在NTFS分区中,同时受到NTFS文件系统的访问控制,因此还需要第2步;
调用DOS命令cacls,赋予Guest用户组读写权限;
如果要取消文件共享,直接调用API函数NetShareDel(),完事。

这就是项目中老版本所采用的方法,从完成后的代码上看,还是比较简单明了的,思路上没有拐多大弯(典型的事后诸葛亮,别人不知道花了多少精力,走了多少弯路才找到这个解决方案,然后把写好的代码拿给你看,你看到代码行数并不算多,就说:这么简单啊~~)。

对于FAT32分区中文件夹,cacls命令是不起作用的,这个命令专用于NTFS格式下的文件和文件夹。也就是说上面这种解决方法的第2步不能用了,那么现在只能使用NetShareAdd()函数添加文件共享。调用此函数后如果不做其他设置,网络用户可以访问共享文件夹,但拥有完全控制权限(这是默认的共享权限设置),而在实际项目中我们希望能够编程控制读写权限。在NTFS分区中,我们还有NTFS文件系统的访问控制功能罩着,在FAT32分区中就只能依赖于操作系统文件夹共享自身的权限设置功能了。

现在开始切入核心问题了,如何编程设置文件夹共享的读写权限?

DOS命令我是不指望了,至少到目前我还没找到能实现这一点的命令;只能靠API,不过一开始,NetShareAdd()这个API就迷惑了我很长时间,晕死~~

NetShareAdd函数有一个参数:LPBYTE buf,是指向一个结构体的指针,如果将level参数设为2,则buf所指结构体的定义如下:

1

typedef struct _SHARE_INFO_2
2





{
3

LPWSTR shi2_netname;
4

DWORD shi2_type;
5

LPWSTR shi2_remark;
6

DWORD shi2_permissions;
7

DWORD shi2_max_uses;
8

DWORD shi2_current_uses;
9

LPWSTR shi2_path;
10

LPWSTR shi2_passwd;
11

} SHARE_INFO_2,
12

*PSHARE_INFO_2,
13

*LPSHARE_INFO_2;
注意其中的一个成员:DWORD shi2_permissions,饿滴神啊,莫非这就是我的救星?也来的太容易了吧!?

但之后反复而痛苦的试验告诉我,actually,It doesn't work!!!无论将这个家伙设成多少,对于函数的执行效果都没什么影响。在CSDN,MSDN,cnblog(呃,貌似园子里没功夫讨论这种鸡毛蒜皮的小问题)上搜索,到处都是关于这个参数的用法的疑问,以及求教如何实现FAT32文件夹只读共享的帖子,后来在MSDN的一个角落里看到说对于不支持user level的操作系统,此参数无效……

希望再次落空,此时,我,一个刚接触Win32 API一个半星期的菜鸟,已经在这个“小问题”上花费了一个星期的时间。此时还真有些诚惶诚恐的感觉,这么简单的问题,搞了一个星期的都没个结果,真应该把你给T了,不过幸好老板没有将我的恐惧转化为他的行动,再次暴汗~~

没办法,只能硬着头皮找其他的路子了。经过反复的找资料,反复的试验,反复的失败,然后再反复的找资料,orz,总算是找到设置共享权限的解决方法了,具体过程还是留给下个帖子吧(我的处女贴就这么点内容,怎么写了这么长时间?)
ps:这里怎么在Normal模式下让两张贴图并排显示?我搞了老半天才在在HTML下弄好,晕~~

上篇中简单的说了下Windows XP中NTFS文件共享和FAT32文件共享的区别,在下半篇中我就不说废话了,直接将设置文件共享的读写权限的关键代码贴出来。

首先是将文件夹设为共享的代码:


//Set a folder for net share


NET_API_STATUS AddNetShare(LPSTR sharedFolderPath, LPSTR shareName)






{


DWORD level = 2;


SHARE_INFO_2 si;


DWORD parmErr = 0;




si.shi2_netname = shareName; //share name


si.shi2_type = STYPE_DISKTREE;


si.shi2_remark = (LPSTR)L"This is a shared folder."; //remark for the shared folder


si.shi2_path = sharedFolderPath; //path of the shared folder


si.shi2_permissions = ACCESS_ALL; //this parameter doesn't work acctually


si.shi2_passwd = NULL; //no password need


si.shi2_max_uses = -1; //unlimited connected


si.shi2_current_uses = 0;




NET_API_STATUS res = NetShareAdd(NULL, level, (LPBYTE)&si, &parmErr);




return res;


}



注意,在上篇中谈到过,SHARE_INFO_2中的shi2_permissions对于Windows XP无效,你可以设为任意值,但对文件夹的共享读写权限不起作用。

如果只调用上面的函数将文件夹设为共享,则默认情况下,网络用户拥有全部读写权限,但很多时候我们不希望网络用户修改共享文件的内容,这样就必须编程将共享权限设置为只读,下面就是设置共享权限的代码:


enum NetShareAccessPermission






{


NetShareReadOnly = 0x001200a9, //readonly permission


NetShareFullControl = 0x001f01ff //full control permission


};




//Set access permission for net shared folder


DWORD SetNetsharePermission(LPTSTR shareName, NetShareAccessPermission permission)






{


DWORD res = 0;


PACL pOldDacl = NULL, pNewDacl = NULL;


PSECURITY_DESCRIPTOR pSD = NULL;


EXPLICIT_ACCESS ea;




res = GetNamedSecurityInfo(shareName,


SE_LMSHARE,


DACL_SECURITY_INFORMATION ,


NULL,


NULL,


&pOldDacl,


NULL,


&pSD);


if (res != ERROR_SUCCESS)






{


goto Cleanup;


}




ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));




ea.grfAccessPermissions = permission; //Set access permission (defined in enum NetShareAccessPermission)


ea.grfAccessMode = SET_ACCESS ;


ea.grfInheritance= CONTAINER_INHERIT_ACE;


ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;


ea.Trustee.ptstrName = L"Everyone";




res = SetEntriesInAcl(1, &ea, pOldDacl, &pNewDacl);


if (res != ERROR_SUCCESS)






{


goto Cleanup;


}




res = SetNamedSecurityInfo(shareName,


SE_LMSHARE,


DACL_SECURITY_INFORMATION ,


NULL,


NULL,


pNewDacl,


NULL);




Cleanup: //Release resource


if (pSD != NULL)






{


LocalFree((HLOCAL) pSD);


}




if (pNewDacl != NULL)






{


LocalFree((HLOCAL) pNewDacl);


}




return res;


}
这段代码最关键的语句是:


ea.grfAccessPermissions = permission;
其中的permission参数是一个自定义的NetShareAccessPermission枚举,如果permission==NetShareReadOnly(即0x001200a9),这样就可以将共享文件夹设为只读了。

但问题的关键是为什么要自己定义一个这样的枚举?0x001200a9和0x001f01ff这两个密码一般的十六进制数是哪里来的?难道MSDN中没有定义一个这样的枚举或宏吗?

实际上,MSDN中的确存在几个预定义的可以为ea.grfAccessPermissions 赋值的宏,上面那两个十六进制数是一个与设置文件系统安全性有关的DWORD开关变量,名为ACCESS_MASK,具体定义可以参考MSDN。win32 API函数的头文件中已经定义了几个可以为这个参数赋值的宏,如GENERIC_READ,KEY_READ等,不过这两个宏在MSDN中也是语焉不详(还是我这个菜鸟实在菜到家了,人家明明说的清清楚楚,就你一个睁眼瞎……),但你可以在VS中输入这两个宏,然后选中后右键转入定义这两个东东的头文件,在头文件有少量的解释。在这个头文件中我们还可以看到更多的预定义的宏。

不幸的是,我没有找到我要的宏,KEY_READ是用来设置注册表只读权限的,GENERIC_READ,呃,我也不知道是用来设置什么的,只知道这两个宏对我的共享权限设置除了引发一些怪异的行为外,没什么帮助。

这样只能靠自己手动的设置那个恐怖的32位的开关变量了。对于一个32位的二进制数,一共有4G种组合,假设我试一种组合要花30s,将吃饭睡觉上XX的时间都搭上,一天24小时,算一下,呃,我要花3800年才试的完……

不过,我可以考虑将这项光荣的任务交给我未来的儿子,然后来个递归,利用“子又有子,子又有孙,子子孙孙无穷匮也,而const不可增”……

不过好在我思维还算缜密,我考虑到无法保证我会有儿子,这样我的递归很可能由于条件不满足产生异常而终止……

我一向不打无把握的仗,想到了一种投机取巧的办法:我可以先通过Windows的UI设置共享文件夹的权限,然后编程获取相关的变量和结构体的值,将这些值保存下来,先不管这些值看得懂看不懂(事实上,不太可能看得懂,又是一些32位的开关量,又是一些3800年),给我们的代码赋值就行了。

我没找到直接获取ACCESS_MASK的API,于是想办法通过VS的调试功能来获取,下面是我用来获取ACCESS_MASK的代码:


//Get the value of access mask


void GetAceTest()






{


DWORD res = 0;


PACL pOldDacl = NULL, pNewDacl = NULL;


PSECURITY_DESCRIPTOR pSD = NULL;


EXPLICIT_ACCESS ea;




ACCESS_ALLOWED_ACE* pAce;




LPTSTR shareName=(LPTSTR)L"TestShareFolder";




res = GetNamedSecurityInfo(shareName,


SE_LMSHARE,


DACL_SECURITY_INFORMATION ,


NULL,


NULL,


&pOldDacl,


NULL,


&pSD);




GetAce(pOldDacl,0,(LPVOID*)&pAce);




ACCESS_MASK Mask=pAce->Mask;




return;


}
ACCESS_MASK是和ACE直接关联的,至于ACE么,呃,说来话长,还是看MSDN吧。

  但要注意的是GetAce()的第二个参数要设为0,因为ACEs是从0开始索引的,如果不小心设为1了,你在VS调试器中看到的东西足够让你郁闷了。

另外,其实还有一个设置文件共享权限的方法。在上面将文件夹设为共享的代码中,将函数NetShareAdd()的第二个参数level设为502,第三个参数设为SHARE_INFO_2,如果将level设为502,第三个参数设为SHARE_INFO_502,就可以通过SHARE_INFO_502这个参数来设定权限了,其定义如下:


typedef struct _SHARE_INFO_502






{


LPWSTR shi502_netname;


DWORD shi502_type;


LPWSTR shi502_remark;


DWORD shi502_permissions;


DWORD shi502_max_uses;


DWORD shi502_current_uses;


LPWSTR shi502_path;


LPWSTR shi502_passwd;


DWORD shi502_reserved;


PSECURITY_DESCRIPTOR shi502_security_descriptor;


} SHARE_INFO_502,


*PSHARE_INFO_502,


*LPSHARE_INFO_502;
其中的PSECURITY_DESCRIPTOR shi502_security_descriptor参数就可以用来设置文件夹共享访问权限,但这个参数也是很难缠,我没有找到直接设置这个参数的方法,只能利用前面那种投机取巧的方法达到目的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: