实现chmod命令
时间:2010-08-04 来源:edsionte
《LinuxC编程实战》一书中在介绍文件操作部分的时候,一开始就给我们举了个实现my_chmod的小程序。它只支持纯数字的权限方式,不支持参数是:权限范围+/-/=权限设置这样的形式。因此我们要实现的my_chmod要能够支持这种形式。具体的功能如下:
my_chmod能够依照权限设置改变文件的权限。权限既可以是代表性的数字,也可以是符号(权限范围+/-/=权限设置)。并且多个符号之间可以用逗号隔开。本文中的my_chmod只支持每个符号中只有一个+/-/=的情况。比如:./my_chmod u+g+o filename 这样的形式本程序并不支持(尽管chmod支持)。
鉴于本博客前文中已经详细讲解了my_cp,因此本文重点说明如何将符号权限设置的方式转化成数字形式。至于符号权限设置的合法性判断等等,无非是利用C语言基础知识来判断,当然这要建立在熟悉chmod命令的基础上。
好了,我们开始吧!
将符号权限转换成数字权限是通过check_option和get_mode两个函数完成的。check_option函数首先判断符号权限的位置合法性。由于多个符号参数可以用逗号隔开,因此在此函数中依次调用get_mode函数,最后将得到最终的mode。比如:./my_chmod u+rw,go+w test.c;那么在check_option函数中分别对两组符号参数(u+rw和go+w)调用get_mode函数。两次调用使得mode为:rw- -w- -w-(622)。但是这时的mode并不是文件最终的mode,还要看文件在执行my_chmod之前的权限,如果test.c 本身权限为644,那么my_chmod执行后,文件权限为666。
当这只是+情况,对于-和=的情况,我们下面会详细说明。
我们再来看get_mode函数的原型:
void get_mode(int u,int g,int o,int r,int w,int x,int sign,mode_t* mode);
这个函数一开始看好像很复杂,因为有众多的参数,其实一点都不复杂。参数u,g,o分别对应用户,用户组,其他成员;而r,w,x当然是可读,可写,可执行;sign根据值1,2,3分别对应的是+,-,=;末尾的mode是将源文件的mode传过来,因为要保存其值,所以传来的是指针。这些变量如果为1那么说明在参数中出现过,否则没有出现。这些变量的赋值在check_option函数中完成。
在get_mode函数中,用一个switch语句来区分+,-,=的不同功能。
switch(sign) { case 1://+ { //the code was omited } break; case 2://- { //the code was omited } break; case 3://= { //the code was omited } break; default: printf("sign error\n"); exit(1); }
首先来看case1,它对应的是+。这里的+比较好理解,就是在文件原来的权限基础上再加上命令中的权限。这里u,g,o三个组分别对应三个if语句,再针对具体一个组,查看这个组具有r,w,x权限的那几个(再分别对应三个if语句)。比如:./my_chmod ug+rwx,o+r test.c;首先他会进入第一个if(u)语句,又因为参数中又出现了rwx,因此if(u)中的三个if语句都会被执行。接着,又会以类似的过程去执行if(g)。这样就完成了一次get_mode的调用,至于o+r,它属于第二次调用get_mode函数。
既然参数中出现了rwx,如何让mode具有对应的rwx权限?如何体现在具体的代码上?我们可以采用或运算,但是注意一定是文件本身的mode分别与相应的权限去或。这里采用了一系列的宏,极大的方便了代码的编写。如果你对此处采用或运算感到困惑,那么不妨手动去试试,即可明白。具体的实现代码如下:
case 1: { if(u) { if(r) *mode|=S_IRUSR; if(w) *mode|=S_IWUSR; if(x) *mode|=S_IXUSR; } if(g) { if(r) *mode|=S_IRGRP; if(w) *mode|=S_IWGRP; if(x) *mode|=S_IXGRP; } if(o) { if(r) *mode|=S_IROTH; if(w) *mode|=S_IWOTH; if(x) *mode|=S_IXOTH; } } break;
接着看case2,对应的符号是-。它与上面的+刚好相反:在文件原来的权限上减去命令中所输入的权限。这个动作在代码中也很好体现,用异或即可。因为1^1=0,1^0=1,这样使得与相应宏异或的那个权限为0。
但是这里还应该注意一个问题,刚才我们所分析的是当这个权限存在的时候,我们可以用异或来做相应的“减”,如果命令中要减去的权限,文件本身就不具有,那么异或后又会使得文件具有了这个权限!因此我们应该在小if语句的判断处加上一个与运算:判断此时文件的权限是否含有要减去的权限。至于这里为何用异或,与运算,请自己举例,手动运算即可明白。
case 2: { if(u) { if(r&&(*mode&S_IRUSR)) *mode^=S_IRUSR; if(w&&(*mode&S_IWUSR)) *mode^=S_IWUSR; if(x&&(*mode&S_IXUSR)) *mode^=S_IXUSR; } if(g) { if(r&&(*mode&S_IRGRP)) *mode^=S_IRGRP; if(w&&(*mode&S_IWGRP)) *mode^=S_IWGRP; if(x&&(*mode&S_IXGRP)) *mode^=S_IXGRP; } if(x) { if(r&&(*mode&S_IROTH)) *mode^=S_IROTH; if(w&&(*mode&S_IWOTH)) *mode^=S_IWOTH; if(x&&(*mode&S_IXOTH)) *mode^=S_IXOTH; } } break;
最后是case3的情况。与上述两种情况不同的是,=并不是在文件原有的权限上增加或减少某个权限,而是对命令中出现的某个组的权限进行赋值。也就是说它会覆盖原组中的权限。比如;已知test.c文件的权限为--wx-w-r--(324)。那么./my_chmod ug=r test.c;后,他的权限为:-r--r--r--(444)。可以看到他将ug两组的原权限覆盖,而o组未在命令中出现,因此不受影响。
在进入某个组时候,比如进入U组时,我们会这样处理mode:*mode&=(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);为何要这么做?就如我们刚才分析的那样,我们要为某个组赋值,必须覆盖掉这个组原有权限,因此我们先得到另外两个组所有权限或的结果(在这个或的结果中,另外两个组所有权限对应的那个位均为1),和文件的原权限去与,便可以使得U组权限为0,而其他组的权限不受影响。这其实根据:1&1=1,1&0=0。
case 3: { if(u) { *mode&=(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH); if(r) *mode|=S_IRUSR; if(w) *mode|=S_IWUSR; if(x) *mode|=S_IXUSR; } if(g) { *mode&=(S_IRUSR|S_IWUSR|S_IXUSR|S_IROTH|S_IWOTH|S_IXOTH); if(r) *mode|=S_IRGRP; if(w) *mode|=S_IWGRP; if(x) *mode|=S_IXGRP; } if(o) { *mode&=(S_IRGRP|S_IWGRP|S_IXGRP|S_IRUSR|S_IWUSR|S_IXUSR); if(r) *mode|=S_IROTH; if(w) *mode|=S_IWOTH; if(x) *mode|=S_IXOTH; } } break; default: printf("sign error\n"); exit(1);
好了,以上便是如何将符号权限转化为数字权限。最终的mode会在check_option函数中得到,这个mode会在chmod函数中用到。