环境:centos 7.3 x86_64
如果搜一下man就会发现,里面有两个chmod,一个是chmod(1)
,一个是chmod(2)
。根据牛顿-莱布尼兹公式,立即推,第一个是用户命令,第二个是系统调用。系统调用里,函数的原型是这样的:
int chmod(const char *pathname,mode_t mode);
所以要实现的chmod命令,无非就是把用户的输入解释成对应的mode_t类型,然后交给chmod系统调用进行处理即可。
目标,支持符号和八进制数两种表示法。符号表示法仅支持[ugoa...][+-=][rwxst...]
。八进制表示法支持1-4位八制数,没有设置的位默认为0,同man 1 chmod
一致。
Usage: chmod mode file...
程序流程大概是这样的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
void oops(const char* str);
mode_t parse_mode(const char *mode);
void xc_chmod(const char *path,mode_t mode);
mode_t modify_mode(mode_t mode);
#define USAGE "Usage: chmod mode file...\n"
enum{
CW_ADD,CW_REMOVE,CW_SET,CW_COVER
}chmod_way;
enum{
MOD_READ = 0444,MOD_WRITE = 0222,MOD_EXEC = 0111,};
enum{
ER_ALL = 0777,ER_USR = 0700,ER_GRP = 070,ER_OTH = 07
}effect_range = 0;
int mode_read = 0;
int mode_write = 0;
int mode_exec = 0;
int mode_sgid = 0;
int mode_sticky = 0;
int main(int ac,char *av[])
{
mode_t mode;
if(ac < 3){
fprintf(stderr,USAGE);
exit(1);
}
mode = parse_mode(*++av);
ac -= 2;
while(ac--)
xc_chmod(*++av,mode);
return 0;
}
void xc_chmod(const char *path,mode_t mode)
{
struct stat info;
if(chmod_way != CW_COVER){
if(stat(path,&info) == -1)
oops("chmod");
mode = info.st_mode;
mode = modify_mode(mode);
}
if(chmod(path,mode) == -1)
oops("chmod");
}
mode_t modify_mode(mode_t mode)
{
mode_t amode = mode;
int add_or_remove;
if(chmod_way == CW_SET)
{
if(mode_read){
// printf("MODE_READ: %o & effect_range: %o = %o\n",MOD_READ,effect_range,MOD_READ& effect_range);
amode |= MOD_READ & effect_range;
}
if(mode_write)
amode |= MOD_WRITE & effect_range;
if(mode_exec)
amode |= MOD_EXEC & effect_range;
if(mode_sgid){
if(effect_range & ER_USR)
amode |= S_ISUID;
if(effect_range & ER_GRP)
amode |= S_ISGID;
}
if(mode_sticky && (effect_range & ER_OTH))
amode |= S_ISVTX;
}else{
add_or_remove = chmod_way == CW_ADD;
if(mode_read)
if(add_or_remove)
amode |= MOD_READ & effect_range;
else
amode &= ~(MOD_READ & effect_range);
if(mode_write)
if(add_or_remove)
amode |= MOD_WRITE & effect_range;
else
amode &= ~(MOD_WRITE & effect_range);
if(mode_exec)
if(add_or_remove)
amode |= MOD_EXEC & effect_range;
else
amode &= ~(MOD_EXEC & effect_range);
if(mode_sgid){
if(effect_range & ER_USR)
if(add_or_remove)
amode |= S_ISUID;
else
amode &= ~S_ISUID;
if(effect_range & ER_GRP)
if(add_or_remove)
amode |= S_ISGID;
else
amode &= ~S_ISGID;
}
if(mode_sticky && (effect_range & ER_OTH))
if(add_or_remove)
amode |= S_ISVTX;
else
amode &= ~S_ISVTX;
}
return amode;
}
mode_t parse_mode(const char *mode)
{
char *p;
mode_t amode;
if(mode[0] >= '0' && mode[0] <= '9'){
/* parse use oct digits */
int len = 0;
int base = 1;
int tmp = 0;
int i;
int legal = 1;
p = mode;
while(*p){
if(!(*p >= '0' && *p <= '7'))
legal = 0;
p++;
len++;
}
if(len > 4)
legal = 0;
if(!legal){
fprintf(stderr,USAGE);
exit(1);
}
sscanf(mode,"%o",&tmp);
amode = tmp;
chmod_way = CW_COVER;
}else{
/* parse use symbolic */
p = mode;
while(*p != '+' && *p != '-' && *p != '='){
switch(*p){
case 'a':
effect_range |= ER_ALL;
break;
case 'g':
effect_range |= ER_GRP;
break;
case 'o':
effect_range |= ER_OTH;
break;
case 'u':
effect_range |= ER_USR;
break;
default:
fprintf(stderr,USAGE);
exit(1);
break;
}
p++;
}
switch(*p){
case '+':
chmod_way = CW_ADD;
break;
case '-':
chmod_way = CW_REMOVE;
break;
case '=':
chmod_way = CW_SET;
break;
default:
fprintf(stderr,USAGE);
exit(1);
break;
}
p++;
while(*p){
switch(*p){
case 'r':
mode_read = 1;
break;
case 'w':
mode_write = 1;
break;
case 'x':
mode_exec = 1;
break;
case 's':
mode_sgid = 1;
break;
case 't':
mode_sticky = 1;
break;
default:
fprintf(stderr,USAGE);
exit(1);
break;
}
++p;
}
}
return amode;
}
void oops(const char* str)
{
perror(str);
exit(1);
}
效果:
测试脚本:
gcc chmod.c
./a.out 7777 aaa
ls -l aaa > bbb
./a.out 644 aaa
ls -l aaa >> bbb
./a.out 0000 aaa
ls -l aaa >> bbb
./a.out u=rwxst aaa
ls -l aaa >> bbb
./a.out g=rwxst aaa
ls -l aaa >> bbb
./a.out o=rwxst aaa
ls -l aaa >> bbb
./a.out 0000 aaa
ls -l aaa >> bbb
./a.out a=rwxst aaa
ls -l aaa >> bbb
./a.out 0000 aaa
ls -l aaa >> bbb
./a.out a=rw aaa
ls -l aaa >> bbb
./a.out 0000 aaa
ls -l aaa >> bbb
./a.out ug=rw aaa
ls -l aaa >> bbb
./a.out o+rw aaa
ls -l aaa >> bbb
./a.out u+x aaa
ls -l aaa >> bbb
./a.out u+s aaa
ls -l aaa >> bbb
./a.out u-x aaa
ls -l aaa >> bbb
./a.out a-r aaa
ls -l aaa >> bbb
./a.out a+t aaa
ls -l aaa >> bbb
chmod 7777 aaa
ls -l aaa > ccc
chmod 644 aaa
ls -l aaa >> ccc
chmod 0000 aaa
ls -l aaa >> ccc
chmod u=rwxst aaa
ls -l aaa >> ccc
chmod g=rwxst aaa
ls -l aaa >> ccc
chmod o=rwxst aaa
ls -l aaa >> ccc
chmod 0000 aaa
ls -l aaa >> ccc
chmod a=rwxst aaa
ls -l aaa >> ccc
chmod 0000 aaa
ls -l aaa >> ccc
chmod a=rw aaa
ls -l aaa >> ccc
chmod 0000 aaa
ls -l aaa >> ccc
chmod ug=rw aaa
ls -l aaa >> ccc
chmod o+rw aaa
ls -l aaa >> ccc
chmod u+x aaa
ls -l aaa >> ccc
chmod u+s aaa
ls -l aaa >> ccc
chmod u-x aaa
ls -l aaa >> ccc
chmod a-r aaa
ls -l aaa >> ccc
chmod a+t aaa
ls -l aaa >> ccc
diff bbb ccc