《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函数中用到。