您的位置:首页 > 运维架构 > Linux

在Linux中, 用C语言简单实现chmod命令

2013-11-22 22:49 806 查看
在Linux中,我们知道chmod这个命令的功能是改变文件的权限。

现在, 用C语言实现这个改变文件权限的功能

要用C语言实现这个功能的核心就是

利用chmod()这个函数及对需要改变的权限的分析


chmod函数原型如下:
#include <sys/types.h>
#include <sys/stat.h>
int chmod(const char *path, mode_t mode)	//第一个形参是要修改权限的文件的文件名, 第二个形参是文件需要写入的权限


首先, chmod命令有2个格式

1)  chmod 777 test.c//数字格式

2) chmod a+x, u+rw test.c //字符格式

因为是2个格式, 所以我们用分别处理它们


首先是"数字格式"

"数字(即777)"是需要写入的权限

因为"数字"是中命令行获取的, 而从命令行获取的信息都是字符串的形式存在的, 所以我们需要将数字(字符串类型), 转换成整型

所以需要用到atoi()函数(atoi()函数功能: 将字符串转换成整型), 执行了mode = atoi("数字")后, mode现在是一个整型了


因为在文件的权限中有3中权限r,(可读) w(可写), x(可执行), 分别对应r = 4, w = 2, x = 1. 

即如果满权限的话就是r+w+x = 7, 无权限的话就是0, 因为文件有3个组, 文件所有者, 组所属, 其他人。 所以最大的权限为777, 最小为000.


因为atoi()函数返回的类型是一个整型数, 即一个十进制

而chmod(const char *path, mode_t mode)中mode需要的数是一个八进制的数


所以我们现在要将mode转换成一个八进制


mode_u = mode / 100;					//文件所有者权限
mode_g = (mode - (mode_u*100))/10;			//所属组权限
mode_o = mode - (mode_u*100) - (mode_g*10)		//其他人权限
mode = (mode_u * 8 * 8) + (mode_g * 8) + mode _o	//转换成八进制


**************************************

 chmod a+x, u+rw test.c//字符格式

首先, 在chmod命令中 +代表添加权限, -代表删除权限, =代表设置权限.

前2个权限很好理解, 但是第3个是什么意思呢, 是这样的

例如 我们有一个文件test.c 它的权限是 rwxrw-rw-

现在我们用chmod u=x test.c 后, 这个文件的权限就变成了--xrw-rw, u=x代表中文件所有者的这个组中, 除了x(可执行)其他权限都被覆盖


对字符格式的分析, 更多的是分析+,--,=这些操作

首先我们要先构造一个架构

即如果是+, 我们应该这么实现, 如果是-, 怎么实现等

case '+'
{
添加权限操作
}
break;
case '-'
{
删除权限操作
}
break;
case '='
{
设置权限操作
}
break;
default:
出错
break;

接下来在填补架构

case '+'
{
//添加权限操作
if(如果u需要被写入权限)
{

}
if(如果g需要被写入权限)
{

}
if(如果o需要被写入权限)
{

}
}
break;
-, = 格式如上

继续填补架构

case '+'
{
//添加权限操作
if(如果u需要被写入权限)
{
if(如果写入权限的权限是r)
;
if(如果写入权限的权限是w)
;
if(如果写入权限的权限是x)
}
if(如果g需要被写入权限)
{
同上;
}
if(如果o需要被写入权限)
{
同上;
}
}
break;
-, = 格式如上

最终架构就是上面那样的一个模型

首先在分析+,-, =的时候, 先说明一下宏

宏宏对应的八进制含义

S_IRWXU  00700 权限,代表该文件所有者具有可读、可写及可执行的权限。

S_IRUSR 或S_IREAD,      00400权限, 代表该文件所有者具有可读取的权限。

S_IWUSR 或S_IWRITE,   00200 权限, 代表该文件所有者具有可写入的权限。

S_IXUSR 或S_IEXEC,      00100 权限, 代表该文件所有者具有可执行的权限。

S_IRWXG 00070权限,代表该文件用户组具有可读、可写及可执行的权限。

S_IRGRP 00040 权限,代表该文件用户组具有可读的权限。

S_IWGRP 00020权限,代表该文件用户组具有可写入的权限。

S_IXGRP 00010 权限,代表该文件用户组具有可执行的权限。

S_IRWXO 00007权限,代表其他用户具有可读、可写及可执行的权限。

S_IROTH 00004 权限,代表其他用户具有可读的权限

S_IWOTH 00002权限,代表其他用户具有可写入的权限。

S_IXOTH 00001 权限,代表其他用户具有可执行的权限。


如果是'+'的话

就是添加权限, 例如, 权限是rw-rw-rw-, 现在chmod u+x后, 权限就变成了rwxrw-rw-  文件所有者组多了可执行权限

还有一种情况就是如果是rwxrw-rw-, 现在执行chmod u+x后, 权限还是rwxrw-rw-, 并没有改变。

很明显, 这个添加的权限操作就是如果有这个权限的话, 我们就添加它, 没有的话就不添加它

所以, 这里用一个或运算就可以实现这个效果 原理是0|1 = 1, 1|1 = 1

即mode |= 要写入的权限


‘+’最终框架
case '+':
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;

接下来分析'-'.

 '-'是删除权限, 即如果权限是r-xrwxrwx的话, 那么chmod u-x后, 权限就是r--rwxrwx

这里就是屏蔽要删除的权限, 在这里我们可以用与运算+反码实现这种操作

原理是0&任何数=0

这里的思路, 例如现在我要删除u的r权限, 而u的r权限的宏S_IXUSR的八进制是00100, 现在将它取反就变成了677 (不管前2个代表什么), 

在没有chmod u-x之前 权限是r-xrwxrwx 即577

现在我们用577&677

101 111 111

& 110 111 111

____________________
100 111 111  = 477

权限477 翻译后, 权限为r--rwxrwx 很明显u的x权限屏蔽了


‘-’最终框架
case '-':
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;

最后分析'='

= 的操作时一个屏蔽权限的过程, 

例如文件test.c的权限是rwxrwxrwx, 执行chmod r=x后, 权限就变成了--xrwxrwx, 即在所有者

的组中rw权限被屏蔽, 而组所属组和其他人组没有发生什么变化

所以这里我们可以将需要覆盖权限的组的rwx全部置0, 其他组不变, 然后在加上需要加入的权



例如在上面的例子中, 我们可以这样做


*mode &= (S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);	//将所有者组中的权限置0


然后加上需要加上的权限, 我们可以用或运算 


*mode |= S_IXUSR;

所以这里的思路就是先将要设置权限的组的所有权限置0, 然后在添加上要设置的权限

‘=’最终框架
case '=':
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;

C语言简单实现chmod的具体代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

void get_mode(int u, int g, int o, int r, int w, int x,
int sign, mode_t *mode)
{
switch(sign)
{
case '+':
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;

case '-':
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;

case '=':
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");
break;
}
}
int main(int argc, char *argv[])
{
int i;
int k;
mode_t mode; //总权限
int mode_u; //所有者权限
int mode_g; //组所属权限
int mode_o; //其他人权限
int flag_u; //标记所有者是否需要设置
int flag_g; //标记组所属是否需要设置
int flag_o; //标记其他人是否需要设置
int r;
int w;
int x;
int sign; //标记+, -, =
struct stat buf; //lstat();

//检查命令行是否输入错误
if(argc < 3)
{
printf("%s <mode> <target file>\n", argv[0]);
exit(0);
}

//有可能是多文件一起使用chmod, 所以用循环执行
for(k = 2; k < argc; k++)
{
//区分出是数字格式, 还是字符串格式
if(argv[1][0] >= '0' && argv[1][0] <= '7') //数字格式
{
mode = (unsigned)atoi(argv[1]); //字符串转换成无符号整型, mode_t是无符号类型
if(mode > 777 || mode < 0) //设置权限出错的话
{
printf("mode num error!\n");
exit(0);
}

//转换成八进制
mode_u = mode / 100;
mode_g = (mode - (mode_u*100)) / 10;
mode_o = mode - (mode_u*100) - mode_g*10;
mode = mode_u*8*8 + mode_g*8 + mode_o;
}
else
{
i = 0;
//获取文件本身的权限
if(lstat(argv[k], &buf) == -1)
{
fprintf(stderr, "line %d ", __LINE__);
perror("lstat");
exit(1);
}
mode = buf.st_mode;
while(argv[1][i] != '\0')
{
r = w = x = 0;
flag_u = flag_g = flag_o = 0;
while(argv[1][i] != ',' && argv[1][i] != '\0')
{
if(argv[1][i] == 'u')
flag_u = 1;
else if(argv[1][i] == 'g')
flag_g = 1;
else if(argv[1][i] == 'o')
flag_o = 1;
else if(argv[1][i] == 'a')
flag_u = flag_g = flag_o = 1;
else if(argv[1][i] == '+')
sign = '+';
else if(argv[1][i] == '-')
sign = '-';
else if(argv[1][i] == '=')
sign = '=';
else if(argv[1][i] == 'r')
r = 'r';
else if(argv[1][i] == 'w')
w = 'w';
else if(argv[1][i] == 'x')
x = 'x';
i++;
}
get_mode(flag_u, flag_g, flag_o, r, w, x, sign, &mode);
if(argv[1][i] == ',')
i++;
}
}

if( chmod(argv[k], mode) == -1)
{
perror("chmod error");
exit(1);
}

}
return 0;
}


代码实现的效果













不是我不想排好版啊, 只是CSDN的排版太烂了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 linux 文件操作