Linux文件系统

本文详细介绍了Linux文件系统的各项操作,包括读取、写入、删除文件,重命名,修改权限,登录与退出,查看文件系统信息等功能。通过具体函数实现,阐述了如何在Linux环境下进行文件操作,涉及inode、数据块、索引节点等概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2021SC@SDUSC


项目环境:

  1. 树莓派4b
  2. Ubuntu Desktop 21.04

读取文件:

下面是读取文件函数 readFile 的实现;在该函数中,文件名 name 作为形参传入。

首先读取 inode :

PInode pInode = new Inode();
getInode(pInode, id);

然后,依次遍历 10 个直接索引,读取数据块。这其中要分几种情况进行讨论。首先,如果文件的长度超过了 blocksize ,那么长度就要更新为原有的长度减去 getData 获取的 blocksize 的长度,若文件长度未超过 blocksize ,则 len 的长度更新为 len 减去 blocks ize 的长度,并将缓冲区删除。之后的遍历一个一级索引也是类似的操作。

for(i = 0; i < 10; i++)
{
    blockId = pInode->addr[i];
    if(blockId > 0)
    {
       if(len > blockSize)
       {
          len -= getData(blockId, buff, blockSize, 0);
          printf("%s", buff);
       }
       else
       {
          len -= getData(blockId, buff, len, 0);
          printf("%s\n", buff);
          delete buff;
          return 0;
       }
     }
     else
     {
       printf("\n");
       delete buff;
       return 0;
     }
}

下面考虑遍历二级索引:

for(i = 0; i < totalItem; i++)
{
    blockId = getItem(addrBlockId, i);
    if(blockId > 0)
    {
       if(len > blockSize)
       {
          len -= getData(blockId, buff, blockSize, 0);
          printf("%s", buff);
       }
       else
       {
          len -= getData(blockId, buff, len, 0);
          printf("%s\n", buff);

          delete buff;
          return 0;
        }
     }
     else
     {
        printf("\n");
        delete buff;
        return 0;
     }
}

与遍历一级索引是类似的。

文件写入:

下面是文件写入 Write 的实现。

首先,我们依然是读取节点 PInode pInode = new Inode(); getInode(pInode, id); 然后,我们定义一个缓冲区 char* buff = new char[blockSize+1]; 对于 10 个直接索引对应的数据块,我们可以进行直接读取。

for(i = 0; i < 10; i++)
{
    blockId = pInode->addr[i];
    if(blockId > 0)
    {
       num = waitForInput(buff, blockSize);
       writeData(blockId, buff, num, 0);
       len += num;
       if(num < blockSize)
       {
          pInode->length = len;
          time(&(pInode->time));
          updateInode(*pInode);
          delete buff;
          return 0;
       }
       else
       {
          printf("You have input %ld Byte dat, continue?[Y/N]", len);
          ch = getchar();
          if(ch != 'Y' && ch != 'y')
          {
             pInode->length = len;
             time(&(pInode->time));
             updateInode(*pInode);
             delete buff;
             return 0;
          }

         }
      }

需要注意,同写入文件一样,需要对需要写入的数据大小与 blocksize 的大小进行一下比较,当 num 小于 blocksize 的时候,可以直接进行写入,并更新索引节点 pnode ,然后删除缓冲区。

写入数据之后,将空闲数据块的数目 blockFree 自减,并对其进行更新,然后更新数据块比特图,然后更新索引节点 pnode 。

if (blockId > 0)
{
    superBlock.blockFree--;
    updateSuperBlock(superBlock);
    blockBitmap[blockId] = 1;
    updateBlockBitmap(blockBitmap, blockId);
    pInode->addr[i] = blockId;
    time(&(pInode->time));
    updateInode(*pInode);
    num = waitForInput(buff, blockSize);
    writeData(blockId, buff, num, 0);
    len += num;
    if (num < blockSize)
    {
        pInode->length = len;
        time(&(pInode->time));
        updateInode(*pInode);
        delete buff;
        return 0;
    }
    else
    {
        printf("You have input %ld Byte dat, continue?[Y/N]", len);
        ch = getchar();
        if (ch != 'Y' && ch != 'y')
        {
            pInode->length = len;
            time(&(pInode->time));
            updateInode(*pInode);
            delete buff;
            return 0;
        }
    }
}

现在,写入一个一级索引:

unsigned addrBlockId = pInode->addr[10];
int itemTotal = blockSize/itemSize;

删除文件:

首先读取 inode:PInode pInode = new Inode(); getInode(pInode, id);

遍历 10 个一级索引,首先 blockId 指向 PNode ,然后一次更新 superblock ,释放 block ,然后更新 blockbitmap。

int i;
unsigned int blockId;

for (i = 0; i < 10; i++)
{
    blockId = pInode->addr[i];

    if (blockId > 0)
    {
        superBlock.blockFree++;
        updateSuperBlock(superBlock);

        releaseBlock(blockId);
        blockBitmap[blockId] = 0;
        updateBlockBitmap(blockBitmap, blockId);
    }
}

释放一级索引块并更新 superblock superBlock.blockFree++; updateSuperBlock(superBlock) 然后释放 block 和 blockbitmap ,然后更新这两个数据项:

releaseBlock(addrBlockId);
blockBitmap[addrBlockId] = 0;
updateBlockBitmap(blockBitmap, addrBlockId);

最后,我们更新目录 inode 以及删除 curlink:

curInode.length
time(&(curInode.time));
updateInode(curInode);
removeFcbLinkNode(curLink, *pInode);

重命名:

这个功能的实现很简单,直接使用 strcpy 函数将 newname 赋予文件即可,然后再进行更新

strcpy(inode.name, newName);
time(&(inode.time));
updateInode(inode);
// update curLink
FcbLink link = curLink->next;
while (link != NULL)
{
    if (strcmp(link->fcb.name, name) == 0)
    {
        strcpy(link->fcb.name, newName);
        return 0;
    }
    link = link->next;
}

修改权限:

现在我们来进行修改文件权限的函数实现 chmod ,它的第一个参数 name 是文件名,即要需要被检查的文件,第二个参数 type 代表需要被规定的新的权限。
具体来讲,我们先获取 inode ,如果这个 isDir 被判定为 1 之后,先调用 cd 函数返回目录,然后初始化一个进程控制块 FCBlink ,然后只要这个链的指向不为空就一直遍历下去,并调用 chmod  函数。最终跳出循环之后,我们将 inode 的 type置为我们想要设置的 type ,并进行更新。

int FileSystem::chmod(char* name, unsigned char type)
{

    unsigned int id = findChildInode(curLink, name);
    if(id > 0)
    {
        Inode inode;
        getInode(&inode, id);

        if(inode.isDir == 1)
        {
            cd(name);
            FcbLink link = curLink->next;
            while(link != NULL)
            {
                chmod(link->fcb.name, type);
                link = link->next;
            }
            cd((char *)"..");
        }

        inode.type = type;
        time(&(inode.time));
        updateInode(inode);
        return 0;
    }
    else
    {
        printf("no such file or directory:%s\n", name);
        return -1;
    }
}

登录与退出:

void FileSystem::login()
{
    char username[10];
    char password[10];
    while(1)
    {
        printf("username:");
        fgets(username, sizeof(username), stdin);
        if(username[strlen(username)-1] == '\n')
            username[strlen(username)-1] = '\0';
        system("stty -echo");
        printf("password:");
        fgets(password, sizeof(password), stdin);
        if(password[strlen(password)-1] == '\n')
            password[strlen(password)-1] = '\0';
        system("stty echo");
        printf("\n");
        if(strcmp(username,user.username)==0 && strcmp(password,user.password)==0)
            break;
        else
        {
            printf("username or password is not correct,please try again.\n");
        }
    }
}
void FileSystem::logout()
{

    updateResource();
    printf("%s has logout\n", user.username);

    openFileSystem();
    login();
}

Updaterecourse 函数用来保存数据,重新打开系统并进行登录验证,由于命令循环未退出,故会继续接受命令。

退出文件系统:

void FileSystem::exit()
{

    printf("Bye!\n");
    stopHandle(0);
}

实现简单,不再赘述。

查看文件系统信息:

查看文件系统信息,分别打印总数据块数目,每个数据块大小,仍然空闲的数据块数量,总索引节点数目。

void FileSystem::systemInfo()
{
    printf("Sum of block number:%d\n",superBlock.blockNum);
    printf("Each block size:%d\n",superBlock.blockSize);
    printf("Free of block number:%d\n",superBlock.blockFree);
    printf("Sum of inode number:%d\n",superBlock.inodeNum);
}

清屏:

void FileSystem::clr()
{
    system("clear");
}

处理用户命令:

其实就是直接使用 switch 语句,对不同的输入命令调用相应的函数进行处理:

switch(analyse(input))
        {
            case HELP:
                help();
                break;
            case LS://执行ls或ls-l
                if(strcmp(cmd[1], "-l") == 0)
                    ls_l();
                else
                    ls();
                break;
            case CD:
                cd(cmd[1]);
                break;
            case MKDIR:
                createFile(cmd[1],1);
                break;
            case TOUCH:
                createFile(cmd[1],0);
                break;
            case CAT:
                read(cmd[1]);
                break;
            case WRITE:
                write(cmd[1]);
                break;
            case RM:
                if(strcmp(cmd[1], "-r") == 0)
                    del(cmd[2]);
                else
                    del(cmd[1]);
                break;
            case MV:
                mv(cmd[1], cmd[2]);
                break;
            case CP:
                if(strcmp(cmd[1], "-r") == 0)
                    cp(cmd[2], cmd[3]);
                else
                    cp(cmd[1], cmd[2]);
                break;
            case CHMOD:
                if(strcmp(cmd[1], "-r") == 0)
                    chmod(cmd[3], cmd[2][0]-'0');
                else
                    chmod(cmd[2], cmd[1][0]-'0');
                break;
            case LOGOUT:
                logout();
                break;
            case EXIT:
                exit();
                break;
            case SYSINFO:
                systemInfo();
                break;
            case CLEAR:
                clr();
                break;
            case ACCOUNT:
                account();
                break;
            default:
                break;
        }
    }while(1);

取出用户数据:

使用 C 语言的内置函数 rewind 返回一个指向当前文件的开头的指针,然后调用 fread 函数进行读取内容到缓冲数组 user 中,这里将 count 参数设置为 1.

void FileSystem::getUser(User* pUser)
{
    if(fp == NULL || pUser == NULL)
    {
        return;
    }
    rewind(fp);
    fread(pUser, userSize, 1, fp);
}

设置用户数据:

实现的原理跟刚才的 get 其实基本一致,只不过得到指向当前文件的开头的指针后,应该向缓冲区中写入内容而不再是读出。

void FileSystem::setUser(User user)
{
    if(fp == NULL)
    {
        return;
    }
    rewind(fp);
    fwrite(&user, userSize, 1, fp);
}

SuperBlock:

void Fvoid FileSystem::getSuperBlock(SuperBlock* pSuper)
{
    if(fp == NULL || pSuper == NULL)
    {
        return;
    }
    fseek(fp, sOffset, SEEK_SET);
    fread(pSuper, superBlockSize, 1, fp);
}


void FileSystem::updateSuperBlock(SuperBlock super)
{
    if(fp == NULL)
    {
        return;
    }
    fseek(fp, sOffset, SEEK_SET);
    fwrite(&super, superBlockSize, 1, fp);
}

大致原理与之前的操作相同,只不过这里使用 fseek 函数,实现对 SEEK_SET 起始,偏移量为 sOffset 个字节处的 superblock ,然后调用 fread 函数对其进行读取。
更新的原理也是一样,只不过在找到 SEEK_SET 起始,偏移量为 sOffset 个字节处的 superblock 之后,需要将读取操作改为写入操作来实现对此处的 superblock 的内容的更新。

空闲分区管理:

unsigned int FileSystem::getAvailableBlockId()
{
    if(superBlock.blockFree <= 0)
    {
        printf("no free block\n");
        return 0;
    }

    int i;
    for(i = 0; i < blockNum; i++)
    {
        if(blockBitmap[i] == 0)
        {
            //printf("find block %d\n", i);
            releaseBlock(i);
            return i;
        }
    }
    printf("no free block\n");
    return 0;
}

如上所示,通过一个 superblock 的对象(也命名为 superblock )的属性 BlockFree 属性来判断是否还有空闲分区。如果该值小于等于 0 ,那么说明已经没有空闲块了,这是打印输出错误信息并且返回值为 0.
接下来就对整个位图进行遍历,并且同时判断 blockBitmap[i] 的值,如果找到一个值为 0 ,则释放该空闲区域,并返回该块的索引。
下面是实现取出位图的操作:

void FileSystem::getBlockBitmap(unsigned char* bitmap)
{
    if(fp == NULL || bitmap == NULL)
    {
        return;
    }
    fseek(fp, bbOffset, SEEK_SET);
    fread(bitmap, blockBitmapSize, 1, fp);
}

这个操作的实现和之前的读取用户文件信息很相似,都是先调用 fseek 找到指定位置区域,然后用 fread 函数进行读取,这里不再赘述。

void FileSystem::updateBlockBitmap(unsigned char* bitmap, unsigned int start, unsigned int count)
{
    if(fp == NULL)
    {
        return;
    }
    fseek(fp, bbOffset+start, SEEK_SET);
    fwrite(bitmap+start, count, 1, fp);
}

上述函数 updateBlockBitmap 实现对比特位图的指定区域的信息更新。

inodeImage区域:

unsigned int FileSystem::getAvailableInodeId()
{
    unsigned int i;
    for(i = 0; i < blockNum; i++)
    {
        if(inodeBitmap[i] == 0)
        {
            return i;
        }
    }
    return 0;
}

在 getAvailableInodeId 函数中,寻找一个空闲的 i 节点块 0号已被分配给根目录,返回值大于 0 表示找到。

接下来的取出,更新,释放等操作与前面对其他区域的读写操作均类似,这里不再赘述,直接上代码。

void FileSystem::getInodeBitmap(unsigned char* bitmap)
{
    if(fp == NULL || bitmap == NULL)
    {
        return;
    }
    fseek(fp, ibOffset, SEEK_SET);
    fread(bitmap, inodeBitmapSize, 1, fp);
}


void FileSystem::updateInodeBitmap(unsigned char* bitmap, unsigned int index)
{
    this->updateInodeBitmap(bitmap, index, 1);
}


void FileSystem::updateInodeBitmap(unsigned char* bitmap, unsigned int start, unsigned int count)
{
    if(fp == NULL)
    {
        return;
    }
    fseek(fp, ibOffset+start, SEEK_SET);
    fwrite(bitmap+start, count, 1, fp);
}


void FileSystem::getInode(PInode pInode, unsigned int id)
{
    if(fp == NULL || pInode == NULL)
    {
        return;
    }
    fseek(fp, iOffset+inodeSize*id, SEEK_SET);
    fread(pInode, inodeSize, 1, fp);
}


void FileSystem::updateInode(Inode inode)
{
    if(fp == NULL )
    {
        return;
    }
    fseek(fp, iOffset+inodeSize*inode.id, SEEK_SET);
    fwrite(&inode, inodeSize, 1, fp);
}


void FileSystem::releaseInode(unsigned int id)
{
    if(fp == NULL )
    {
        return;
    }
    fseek(fp, iOffset+inodeSize*id, SEEK_SET);
    int i;
    for(i = 0; i < inodeSize; i++)
    {
        fputc(0, fp);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值