-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUFS.c
More file actions
1607 lines (1516 loc) · 66.2 KB
/
UFS.c
File metadata and controls
1607 lines (1516 loc) · 66.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Author:王琛 202130441801
// Github: https://github.com/Wangccchen/sampleFileSystem
#define FUSE_USE_VERSION 31
#include <fuse3/fuse.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <malloc.h>
#include <math.h>
#define BLOCK_SIZE 512
#define FS_SIZE 8 * 1024 * 1024
#define INODE_SIZE 64
#define SUPER_BLOCK 1
#define ROOT_DIR_BLOCK 1 // 根目录块
#define INODE_BITMAP_BLOCK 1 // inode位图块数
#define DATA_BITMAP_BLOCK 4 // 数据块位图 块数
#define INODE_AREA_BLOCK 512 // inode区块数
#define MAX_DATA_IN_BLOCK 512 // 一个数据块实际能装的大小
#define BLOCK_NUMS (8 * 1024 * 1024 / BLOCK_SIZE) // 文件系统的总块数8MB/512B
#define INODE_BITMAP_START_NUM SUPER_BLOCK // INODE位图区开始的块号
#define DATA_BITMAP_START_NUM (SUPER_BLOCK + INODE_BITMAP_BLOCK) // 数据位图区开始的块号
#define INODE_BLOCK_START_NUM (SUPER_BLOCK + INODE_BITMAP_BLOCK + DATA_BITMAP_BLOCK) // INODE区开始的块号
#define DATA_BLOCK_START_NUM (SUPER_BLOCK + INODE_BITMAP_BLOCK + DATA_BITMAP_BLOCK + INODE_AREA_BLOCK) // 数据区开始的块数 //系统总块数
#define DATA_AREA_BLOCK (BLOCK_NUMS - SUPER_BLOCK - INODE_BITMAP_BLOCK - DATA_BITMAP_BLOCK - INODE_AREA_BLOCK) // 剩下的空闲块数用作数据区的块
#define FILE_DIRECTORY_SIZE sizeof(struct file_directory)
#define MAX_FILENAME 8
#define MAX_EXTENSION 3
#define MAX_DIR_IN_BLOCK (MAX_DATA_IN_BLOCK / FILE_DIRECTORY_SIZE)
#define INODE_NUMS_IN_BLOCK (BLOCK_SIZE / INODE_SIZE)
// 用于一级二级三级索引的数据信息
#define FIRST_INDEX_NUMS 4
#define SINGLE_BLOCK_STORE_NO_NUMS 256
#define SECONDARY_INDEX_NUMS (FIRST_INDEX_NUMS + SINGLE_BLOCK_STORE_NO_NUMS)
#define TRIPLE_INDEX_NUMS (SECONDARY_INDEX_NUMS + SINGLE_BLOCK_STORE_NO_NUMS * SINGLE_BLOCK_STORE_NO_NUMS)
// 用于判断inode对应的文件是文件还是目录
#define IS_DIRECTORY(mode) (((mode)&S_IFMT) == S_IFDIR)
#define IS_REGULAR_FILE(mode) (((mode)&S_IFMT) == S_IFREG)
// 数据结构的定义
#pragma region
// 超级块sb 占用一个磁盘块 总字节为72B
struct sb
{
long fs_size; // 文件系统的大小,以块为单位
long first_blk; // 数据区的第一块块号,根目录也放在此
long datasize; // 数据区大小,以块为单位
long first_inode; // inode区起始块号
long inode_area_size; // inode区大小,以块为单位
long fisrt_blk_of_inodebitmap; // inode位图区起始块号
long inodebitmap_size; // inode位图区大小,以块为单位
long first_blk_of_databitmap; // 数据块位图起始块号
long databitmap_size; // 数据块位图大小,以块为单位
};
// Inode 占用一个磁盘块 总字节为64B
struct inode
{
short int st_mode; /* 权限,2字节 */
short int st_ino; /* i-node号,2字节 */
char st_nlink; /* 连接数,1字节 */
uid_t st_uid; /* 拥有者的用户 ID ,4字节 */
gid_t st_gid; /* 拥有者的组 ID,4字节 */
off_t st_size; /*文件大小,4字节 */
struct timespec st_atim; /* 16个字节time of last access */
short int addr[7]; /*磁盘地址,14字节*/
};
// 记录目录信息的数据结构
struct file_directory
{
// 文件名占用前8字节,拓展名占用3字节
char fname[MAX_FILENAME]; // 文件名
char fext[MAX_EXTENSION]; // 扩展名
short int st_ino; // inode占用2字节 表示inode的块号(从0开始)
char standby[3]; // 备用占用最后3字节
};
// 存放文件的数据块,抽象成一个数据结构
struct data_block
{
char data[MAX_DATA_IN_BLOCK]; // 一个块里面实际能存的数据大小 512B
};
#pragma endregion
// 我的磁盘8M文件路径
char *disk_path = "/home/wc/桌面/SFS/disk.img";
// 辅助函数声明
int read_block_by_no(struct data_block *dataB_blk, short no); // 该函数用于根据数据块号读取对应的数据块
int read_inode_by_no(struct inode *ind, short no); // 该函数用于根据inode号读取对应的inode
int read_inode_by_fd(struct inode *ind, struct file_directory *fd); // 该函数用于根据给定的fd来读取对应的inode
int read_block_by_ind_and_indNo(struct inode *ind, short indNo, struct data_block *blk); // 该函数用于根据给定的inode和inode对应的数据块的块号(相对与inode对应的总的所有数据块)来获取对应文件的数据块
int write_block_by_no(struct data_block *dataB_blk, short no); // 该函数用于根据数据块号来对对应的数据块进行写入
int write_inode_by_no(struct inode *ind, short no); // 该函数用于根据inode号来对inode写入
int write_block_by_ind_and_indNo(struct inode *ind, int indNo, struct data_block *blk); // 该函数用于根据给定的inode和inode对应的数据块的块号(相对与inode对应的总的所有数据块)来写回对应文件的数据块
int determineFileType(const struct inode *myInode);
int is_inode_exists_fd_by_fname(struct inode *ind, const char *fname, struct file_directory *fd, int *blk_no, int *offsize);
int is_same_fd(struct file_directory *fd, const char *fname);
// 功能函数声明
int get_fd_to_attr(const char *path, struct file_directory *attr);
int get_parent_and_fname(const char *path, char **parent_path, char **fname);
int get_info_by_path(const char *path, struct inode *ind, struct file_directory *fd);
int create_file_dir(const char *path, int flag);
int remove_file_dir(struct inode *ind, const char *filename, int flag);
int create_new_fd_to_inode(struct inode *ind, const char *fname, int flag);
int create_dir_by_ino_number(char *fname, char *ext, short int ino_no, char *exp, struct file_directory *fd);
int clear_inode_map_by_no(const short st_ino);
int get_blk_no_by_indNo(struct inode *ind, const short int indNo); // 根据inode对应的文件的数据块的相对块号,来获取对应数据块的绝对块号
short assign_block();
short assign_inode();
int sec_index(short int addr, int offset, short int blk_offset); // 该函数用于获取二级索引块中的一级索引地址
int fir_index(short int addr, int offset, short int blk_offset); // 该函数用于获取一级索引块中的直接索引地址
int zerobit_no(unsigned char *p);
// // 要实现的核心文件系统函数在此,fuse会根据命令来对我们编写的函数进行调用
// static struct fuse_operations SFS_oper = {
// .init = SFS_init, // 初始化
// .getattr = SFS_getattr, // 获取文件属性(包括目录的)
// .mknod = SFS_mknod, // 创建文件
// .unlink = SFS_unlink, // 删除文件
// .open = SFS_open, // 无论是read还是write文件,都要用到打开文件
// .read = SFS_read, // 读取文件内容
// .write = SFS_write, // 修改文件内容
// .mkdir = SFS_mkdir, // 创建目录
// .rmdir = SFS_rmdir, // 删除目录
// .access = SFS_access, // 进入目录
// .readdir = SFS_readdir, // 读取目录
// };
// 核心函数的实现
#pragma region
// 对初始化函数SFS_init的实现
static void *SFS_init(struct fuse_conn_info *conn)
{
// 其实这个init函数对整个文件系统左右不算太大,因为我们要得到的文件系统大小的数据
// 其实在宏定义已经算出来了
// 只不过该文件系统必须执行这个函数,所以只能按步骤走
printf("SFS_init函数开始\n\n");
FILE *fp = NULL;
fp = fopen(disk_path, "r+");
if (fp == NULL)
{
fprintf(stderr, "错误:打开文件失败,文件不存在,函数结束返回\n");
return;
}
// 首先读取超级块里面的内容获取文件系统的信息
struct sb *sb_blk = malloc(sizeof(struct sb));
fread(sb_blk, sizeof(struct sb), 1, fp);
fclose(fp);
// 下面随便输出一些超级块的内容,确定读写是否成功
printf("该文件系统的总块数为%ld\n", sb_blk->fs_size);
// 测试完成可以free掉
free(sb_blk);
printf("SFS_init函数结束\n");
return 0;
}
/*struct stat {
mode_t st_mode; //文件对应的模式,文件,目录等
ino_t st_ino; //inode节点号
dev_t st_dev; //设备号码
dev_t st_rdev; //特殊设备号码
nlink_t st_nlink; //文件的连接数
uid_t st_uid; //文件所有者
gid_t st_gid; //文件所有者对应的组
off_t st_size; //普通文件,对应的文件字节数
time_t st_atime; //文件最后被访问的时间
time_t st_mtime; //文件内容最后被修改的时间
time_t st_ctime; //文件状态改变时间
blksize_t st_blksize; //文件内容对应的块大小
blkcnt_t st_blocks; //文件内容对应的块数量
};*/
// 读取文件属性的函数SFS_getattr,并且赋值给stbuf
// 查找输入的路径,确定它是一个目录还是一个文件。
// 如果是目录,返回适当的权限。如果是文件,返回适当的权限以及实际大小。
static int SFS_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
printf("SFS_getattr函数执行\n\n");
// 通过对应的inode来判断文件的属性
struct inode *ino_tmp = malloc(sizeof(struct inode));
read_inode_by_no(ino_tmp, 0);
printf("SFS_getattr:拿到根目录inode号:%d,大小为:%d\n", ino_tmp->st_ino, ino_tmp->st_size);
// 重新设置stbuf的内容
memset(stbuf, 0, sizeof(struct stat));
struct file_directory *t_file_directory = malloc(sizeof(struct file_directory));
int res = get_info_by_path(path, ino_tmp, t_file_directory);
// 非根目录
if (res != 0)
{
free(t_file_directory);
free(ino_tmp);
printf("SFS_getattr:get_info_by_path时没找到文件或超出层级限制,函数结束返回\n");
return res;
}
// 读取了path下对应的inode和fd,赋值给stbuf
// 读取inode的数据并赋给stbuf
// stbuf->st_ino = ino_tmp->st_ino;
// stbuf->st_atime = ino_tmp->st_atim;
// 下面判断文件是 目录 还是 一般的文件
// 并且修改stbuf对应的权限模式
// 0666代表允许所有用户读取和写入目录,权限位是rw-rw-rw-
// 0766表示权限位是rwxrw-rw-
// 根据返回值来判断
int fileType = determineFileType(ino_tmp);
int ret = 0; // 设置该函数的返回值
switch (fileType)
{
case 1:
printf("这是一个名为%s的目录,inode号为%d\n", t_file_directory->fname, t_file_directory->st_ino);
// 目录通常需要具有可读(列出目录内容)和可执行(能进入目录)权限
// 典型的目录权限是755,表示用户可读写执行,组和其他用户只读执行
stbuf->st_mode = S_IFDIR | 0755;
break;
case 2:
printf("这是一个文件名为%s的文件,inode号为%d\n", t_file_directory->fname, t_file_directory->st_ino);
// 典型的文件权限通常是644,表示用户可读写,组和其他用户只读
stbuf->st_mode = S_IFREG | 0644;
stbuf->st_size = ino_tmp->st_size;
break;
default:
printf("文件不存在!!\n");
ret = -ENOENT;
break;
}
free(t_file_directory);
free(ino_tmp);
printf("SFS_getattr函数执行结束\n\n");
return ret;
}
// SFS_readdir: 根据输入路径来读取该目录,并且显示目录内的内容
// 在终端中输入ls -l,libfuse会调用该函数
// 1.根据path拿到对应的inode
// 2.读取目录inode对应的目录数据块
// 3.遍历该数据块下的所有目录并且显示
static int SFS_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
printf("SFS_readdir开始执行!\n");
struct inode *tinode = malloc(sizeof(struct inode));
struct data_block *blk = malloc(sizeof(struct data_block));
struct file_directory *tfd = malloc(sizeof(struct file_directory));
// 调用上面刚刚实现的辅助函数
// 打开path指定的inode
if (get_info_by_path(path, tinode, tfd) != 0)
{
printf("readdir:找不到该文件!\n");
free(tinode);
free(blk);
free(tfd);
return -ENOENT;
}
// 拿到该inode之后,要保证对应的一定为目录!
if (determineFileType(tinode) != 1)
{
// 如果返回值不为 1 ,说明inode对应的不是目录
printf("readdir:%s下对应的不是目录!\n", path);
free(tinode);
free(tfd);
free(blk);
return -ENOENT;
}
// 对buf里面的先使用filter函数添加 . 和 ..
filler(buf, ".", NULL, 0, 0);
filler(buf, "..", NULL, 0, 0);
// // 设置一个装文件名的char数组
char *name = malloc(MAX_FILENAME + MAX_EXTENSION + 2);
memset(name, '\0', MAX_FILENAME + MAX_EXTENSION + 2);
// 接下来的操作就是对inode中的每一个目录项进行提取对应的fd
// 拿到对应的filename,然后放入buf中
// 具体操作为:先利用for循环拿到inode目录项下的每一个数据块
// 然后依次读取数据块存储的fd,提取其名字放入buf
int fd_num = (tinode->st_size) / sizeof(struct file_directory); // inode一共有多少个fd
int fd_in_blk = fd_num; // 在第二层for循环中读取每个数据块中的数量的临时变量
int blk_num = (tinode->st_size + MAX_DATA_IN_BLOCK - 1) / MAX_DATA_IN_BLOCK; // inode的目录项占有多少个数据块
for (int i = 0; i < blk_num; i++)
{
// 依次读取inode下的每个数据块
read_block_by_ind_and_indNo(tinode, i, blk);
// 把blk分解成fd单位来遍历
struct file_directory *fd = (struct file_directory *)(blk->data);
if (fd_in_blk < MAX_DIR_IN_BLOCK)
{
// 如果inode对应的fd数量没有装满一个块
for (int j = 0; j < fd_in_blk; j++)
{
// 读取该fd的名字
// 拼接fd的文件名
strncpy(name, fd->fname, 8);
// 检查拓展名字段是否为空
if (strlen(fd->fext) != 0)
{
strcat(name, ".");
strncat(name, fd->fext, 3);
}
else
{
printf("SFS_readdir:文件或目录:%s 没有拓展名!", name);
}
filler(buf, name, NULL, 0, 0); // 写入到buf中
fd++; // 指针偏移到blk中的下一个fd
}
}
else
{
// inode对应的fd装满了一个块
for (int j = 0; j < MAX_DIR_IN_BLOCK; j++)
{
// 读取该fd的名字
// 拼接fd的文件名
strncpy(name, fd->fname, 8);
// 检查拓展名字段是否为空
if (strlen(fd->fext) != 0)
{
strcat(name, ".");
}
strncat(name, fd->fext, 3);
filler(buf, name, NULL, 0, 0); // 写入到buf中
fd++; // 指针偏移到blk中的下一个fd
}
// 剩余的没有遍历的inode中的fd数量更新
fd_in_blk -= MAX_DIR_IN_BLOCK;
}
}
free(blk);
free(tfd);
free(tinode);
printf("SFS_readdir执行完成!\n");
return 0;
}
// SFS_mkdir:将新目录添加到根目录,并且更新.directories文件
// 创建目录
static int SFS_mkdir(const char *path, mode_t mode)
{
return create_file_dir(path, 2);
}
// SFS_rmdir:删除空目录
// 获取父目录的inode
// 在父目录的inode中遍历找到输入path的inode,对其进行判断是否为目录
// 设置两个位图区
static int SFS_rmdir(const char *path)
{
// 首先获取父目录路径和文件名
char *par_path = NULL;
char *fname = NULL;
get_parent_and_fname(path, &par_path, &fname);
// 根据父目录来获取父目录的inode和fd
struct file_directory *tfd = malloc(sizeof(struct file_directory));
struct inode *tinode = malloc(sizeof(struct inode));
get_info_by_path(par_path, tinode, tfd);
// 要对此时父目录的inode判断,是否为一个目录而不是文件
if (determineFileType(tinode) != 1)
{
// 不为目录
free(tfd);
free(tinode);
return -ENOENT;
}
// flag置为2表示删除空目录
int res = remove_file_dir(tinode, fname, 2);
free(tfd);
free(tinode);
return res;
}
// SFS_mknod:创建一个新的文件
static int SFS_mknod(const char *path, mode_t mode, dev_t dev)
{
return create_file_dir(path, 1);
}
// SFS_unlink:删除文件
static int SFS_unlink(const char *path)
{
// 首先获取父目录路径和文件名
char *par_path = NULL;
char *fname = NULL;
get_parent_and_fname(path, &par_path, &fname);
// 根据父目录来获取父目录的inode和fd
struct file_directory *tfd = malloc(sizeof(struct file_directory));
struct inode *tinode = malloc(sizeof(struct inode));
get_info_by_path(par_path, tinode, tfd);
// 要对此时父目录的inode判断,是否为一个目录而不是文件
if (determineFileType(tinode) != 1)
{
// 不为目录
free(tfd);
free(tinode);
return -ENOTDIR;
}
// flag置为1表示删除文件
int res = remove_file_dir(tinode, fname, 1);
free(tfd);
free(tinode);
return res;
}
// SFS_access:进入目录
static int SFS_access(const char *path, int flag)
{
printf("SFS_access:进入目录了!\n");
return 0;
}
// SFS_open:打开文件时的操作
static int SFS_open(const char *path, struct fuse_file_info *fi)
{
printf("SFS_open:打开了文件!\n");
return 0;
}
// SFS_read:读取文件的操作
// 根据path找到该文件的inode
// 根据offset偏移量,读取size大小的数据写入buf
static int SFS_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
// 首先根据path获取文件的inode
struct inode *tind = malloc(sizeof(struct inode));
struct file_directory *tfd = malloc(sizeof(struct file_directory));
get_info_by_path(path, tind, tfd);
// 判断该文件是否为目录
if (determineFileType(tind) != 2)
{
free(tind);
return -EISDIR;
}
printf("从inode号为%d,偏移%ld的位置读取大小为%ld的文件内容\n", tind->st_ino, offset, size);
// 读取的大小size要小于inode文件的大小tind->st_size
// 并且offset不能超过该文件inode所示的大小tind->st_size
size_t read_size = size < (tind->st_size - offset) ? size : (tind->st_size - offset);
read_size = read_size < 0 ? 0 : read_size; // 保证真实读取的大小要大于0
// 下面追踪要读取的文件所在数据块的位置信息
int blk_no = offset / MAX_DATA_IN_BLOCK; // 获取数据块号(在inode里面)
int p_in_blk = offset % MAX_DATA_IN_BLOCK; // 在当前块中的下标指向
size = read_size; // 更新读取文件的大小
off_t buf_index = offset; // 读入buf的下标
struct data_block *tblk = (struct data_block *)malloc(sizeof(struct data_block));
// 利用while循环不断读入新的数据块
// 根据size和p_in_blk来找到写入的位置
// 然后将size大小的数据写入buf
while (buf_index < size)
{
// 读入块
read_block_by_ind_and_indNo(tind, blk_no, tblk);
int max_read = MAX_DATA_IN_BLOCK - p_in_blk; // 在当前块中最多能读取的字节
// 看看是否超出了本来要读取的size的大小的量
if (max_read > size)
{
max_read = size;
}
// 写入buf
memcpy(buf + buf_index, tblk->data + p_in_blk, max_read);
// 读入buf之后,对对应的下标指针都进行相应大小的增加
size -= max_read; // 要读的数据减少已读的数据的大小
p_in_blk = 0; // 下标置为起始位置
blk_no++; // 读取下一个块
buf_index += max_read; // 指针移动读取字节数个单位
}
// 读取完成,释放
free(tind);
free(tfd);
free(tblk);
return read_size;
}
// SFS_write:将buf内容写入文件
// 根据path找到该文件的inode
// 根据offset偏移量,读取buf中size大小的数据
// 写入对应文件的inode和数据块
static size_t SFS_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
printf("SFS_write开始执行!\n");
// 首先获得该文件的inode
struct inode *tind = malloc(sizeof(struct inode));
struct file_directory *tfd = malloc(sizeof(struct file_directory));
get_info_by_path(path, tind, tfd);
// 处理写入的块号,在写入块中的下标,在buf中的下标,偏移量
int blk_no = offset / MAX_DATA_IN_BLOCK; // 在inode中的块号
int p_in_blk = offset % MAX_DATA_IN_BLOCK; // 在当前块中的位置
off_t write_off = offset; // 要写入的位置的偏移
char *buf_index = buf; // buf的下标
printf("SFS_write:将buf的内容: %s 写入inode号为: %d ,inode对应的数据块号: %d ,从偏移: %ld 写入\n", buf, tind->st_ino, blk_no, offset);
size_t res = size;
size_t t_size = size;
printf("SFS_write:buf内要对文件: %s 进行写入,内容大小为: %ld\n", tfd->fname, size);
// 判断要写入的位置是否越界(超出文件大小)
if (offset > tind->st_size)
{
// offset超出文件大小
// 但是仍需追加,不直接返回
res = -EFBIG;
}
struct data_block *tblk = malloc(sizeof(struct data_block));
// 利用while循环不断读入数据块
// 根据size和p_in_blk写入当前的块
while (t_size > 0)
{
read_block_by_ind_and_indNo(tind, blk_no, tblk);
// 判断一下写入的文件大小和当前块剩余的大小
int max_write = (MAX_DATA_IN_BLOCK - p_in_blk) < t_size ? (MAX_DATA_IN_BLOCK - p_in_blk) : t_size; // 剩余可写入的数据大小
// 进行写入的操作
memcpy(tblk->data + p_in_blk, buf_index, max_write); // 从tblk要写入的位置,写入max_write数据大小
// 更新该文件的ind信息
tind->st_size = tind->st_size > (write_off + max_write) ? tind->st_size : (write_off + max_write);
// 把该更新的inode写回磁盘
write_block_by_ind_and_indNo(tind, blk_no, tblk);
// 对下标和大小进行更新
write_off += max_write; // 当前偏移量增加写的大小
buf_index += max_write; // buf偏移量增加写的大小
t_size -= max_write; // 要写入的总数减少
blk_no++; // 读取下一个块的块号
p_in_blk = 0; // 从数据块起始位置开始
}
// 把更新后的ind信息写回磁盘
write_inode_by_no(tind, tind->st_ino);
// 释放内存
free(tind);
free(tfd);
free(tblk);
return res;
}
#pragma endregion
// 功能函数的定义
// 根据传入的文件路径path找到对应的fd然后传给attr
int get_fd_to_attr(const char *path, struct file_directory *attr)
{
printf("get_fd_to_attr函数运行!\n\n");
printf("查询的路径为%s\n\n", path);
// 先读取超级块
// 获得根目录在数据区的位置(根据块数)
struct sb *super_blk;
struct data_block *data_blk;
data_blk = malloc(sizeof(struct data_block));
// 利用read_block_by_no函数读取超级块
// 超级块的块号为0,刚好直接查询并且读到创建的data_blk中
int tmp = read_block_by_no(data_blk, 0);
// 根据查询的结果来判断是否读取成功
if (tmp == -1)
{
printf("读取超级块失败!\n\n");
free(data_blk);
return -1;
}
// 读取成功
super_blk = (struct sb *)data_blk;
// 检查路径
// 如果路径为空,则出错返回1
char *tmp_path = strdup(path);
if (!tmp_path)
{
printf("错误:get_fd_to_attr:路径为空,函数结束返回\n\n");
free(super_blk);
return -1;
}
// 根据路径来寻找对应的inode和fd文件
// 创建临时的inode和file_directory来接收
struct inode *tinode = malloc(sizeof(struct inode));
struct file_directory *tfd = malloc(sizeof(struct file_directory));
// 通过get_info_by_path函数找到路径对应文件的inode和file_directory,并且读取到临时的文件当中
int flag = get_info_by_path(path, tinode, tfd);
// 根据返回值判断是否读取成功
if (flag != 0)
{
// 说明读取出现错误
printf("读取对应的inode时出现问题,错误代码为%d!\n", flag);
return flag;
}
// 读取成功,把fd的内容赋值给attr返回
strcpy(attr->fname, tfd->fname);
attr->st_ino = tfd->st_ino;
// strcpy(attr->st_ino,tfd->st_ino);
strcpy(attr->fext, tfd->fext);
strcpy(attr->standby, tfd->standby);
// 释放内存
free(data_blk);
free(tinode);
free(tfd);
return 0;
}
// 该函数根据输入文件的路径找到对应的inode和fd
int get_info_by_path(const char *path, struct inode *ind, struct file_directory *fd)
{
// 查找路径path只会显示文件系统下的路径
// 意思是把文件系统挂载的路径当成根目录
printf("get_info_by_path函数开始执行,文件的路径为%s\n\n", path);
// 对根目录的判断
if (strcmp(path, "/") == 0)
{
// 为根目录
printf("get_info_by_path:输入的路径为根目录:%s,函数已经执行\n", path);
// 根目录的inode号为0
read_inode_by_no(ind, 0);
// 设置一下根目录的文件返回
strcpy(fd->fname, path);
fd->st_ino = 0;
return 0;
}
// 不是根目录
// 文件查找需要一层一层查找
// 先拿到根目录的inode(inode号为0)
read_inode_by_no(ind, 0);
printf("拿到根目录inode,大小为:%d\n", ind->st_size);
// 开始对路径进行分析
char *tmp_path = NULL, *next_level = NULL, *cur_level = NULL, *next_ext = NULL; // tmp_path用于临时记录路径
tmp_path = strdup(path);
tmp_path++; // 去掉最前面的一个"/"
next_level = tmp_path; // 先把下级目录指向根目录下的第一个目录文件
int flag = 0; // 是否找到文件 的表示
int layer_level = 0; // 搜索的层级数
// 逐层检查(利用while循环)
while (next_level != NULL)
{
cur_level = next_level;
next_level = strchr(next_level, '/'); // 检查是否存在下级目录
next_ext = '\0';
if (next_level == NULL)
{
// 此时已到达输入路径的最底层
// cur_level就为最终要找的文件
flag = 1;
// cur_level = strdup(tmp_path); //此时cur_level为目录的名字
// temp_path = strchr(temp_path, '/');
}
else
{
// 还存在下一级目录
// 首先分割下级目录和当前目录
*next_level = '\0';
printf("get_info_by_path:分割后,输出本层目录的名字cur_level:%s\n", cur_level);
// 把指针指向下级目录起始位置
next_level++;
// tmp_path = strchr(tmp_path, "/");
// char *tmp_next_ext = cur_level;create_new_fd_to_inode
// tmp_next_ext = strchr(cur_level, "/");
// // 分割当前目录和下级目录
// if (tmp_next_ext)
// *tmp_next_ext = '\0';
}
// 搜索的层数+1
layer_level++;
printf("输出当前层级:%d\n", layer_level);
// 判断此时的层级是否超出限制
// 若超出了限制,就不进行下面的新建操作
if (layer_level >= 4)
{ // 超出了限制,直接返回
printf("get_info_by_path:超出层级限制无法继续查找文件!\n");
return -EPERM;
}
// 要对当前cur_level指向的fd进行判断,是文件还是目录
// 只有目录才能进行下一层的遍历
if (determineFileType(ind) == 2 && flag != 1)
{
// 如果当前指向的fd是文件,并且没有还有下一级
// 说明无法继续遍历,直接返回
printf("路径中存在不可继续遍历的文件!路径无效\n");
return -ENOENT;
}
// 已经遍历到最底层
if (flag)
{
// 如果已经找到该文件的目录
// 对文件及其后缀名进行分割
char *tmp_dot = strchr(cur_level, '.');
// 首先保存原来的全名
char *tname = strdup(cur_level);
if (tmp_dot != NULL)
{
*tmp_dot = '\0'; // 把文件名和后缀名分开成两个串
// 此时cur_level单独指向文件名
tmp_dot++;
next_ext = tmp_dot;
printf("get_info_by_path:已通过路径提取出文件名!文件名为:%s,拓展名为%s\n", cur_level, next_ext);
// 再把当前文件名重新指向原来的名字
cur_level = tname;
}
else
{
printf("get_info_by_path:已通过路径提取出文件名!文件名为:%s,文件名长度为:%d,无拓展名!\n", cur_level, strlen(cur_level));
// 把当前文件名限定在8个字节
}
printf("get_info_by_path:此时保存原文件全名为:%s\n", tname);
}
int isFindInode = 0; // 是否寻找到当前层级下curlevel的inode
// 下面开始通过cur_level的目录名和inode,查找该inode下是否有该cur_level
// 若找到,则把对应目录或者文件的fd返回到传入的buff:fd中
printf("get_info_by_path:传入is_inode_exists_fd_by_fname:原文件全名为:%s\n", cur_level);
isFindInode = is_inode_exists_fd_by_fname(ind, cur_level, fd, 0, 0);
if (isFindInode == 1)
{
// 存在cur_level这个目录或者文件
// 获取fd对应的inode,更新当前的ind,作为下一层的遍历
read_inode_by_fd(ind, fd);
}
else
{
// 在inode里面找不到cur_level的fd
// 直接返回
printf("get_info_by_path:inode下没有对应名为:%s的文件或目录!\n", cur_level);
return -ENOENT;
}
if (flag != 1 && layer_level < 4)
{
printf("get_info_by_path:当前目录层级为:%d,将从当前目录:%s 的子目录%s 继续寻找!\n", layer_level, cur_level, next_level);
}
else
{
printf("get_info_by_path:文件已找到\n");
break;
}
}
// 此时退出的情况为已找到文件
// printf("get_info_by_path:文件已经找到!\n");
return 0;
}
// 该函数用于分割路径得到父目录和要创建的文件名或目录名
int get_parent_and_fname(const char *path, char **parent_path, char **fname)
{
printf("get_parent_and_fname函数被调用!输入的路径为: %s\n", path);
char *tmp_path = strdup(path);
// 利用strrchr移动到最后一级(文件名或目录名)来取得信息
// 首先判断path是不是根目录
*parent_path = tmp_path; // 记录父目录
tmp_path++; // 跳过第一个"/"
*fname = strrchr(tmp_path, '/'); // 把fname移动到最后一级的/上面
if (*fname != NULL)
{
// 创建的文件不在根目录下
**fname = '\0'; // 分割父目录和文件名
(*fname)++; // 指针移动到文件名上
}
else
{
// 路径指示文件在根目录下
*fname = tmp_path; // 此时tmppath就是文件名
*parent_path = strdup("/"); // 直接设置父目录为根目录
}
printf("父目录为:%s,创建的文件名或目录名为:%s\n", *parent_path, *fname);
return 0;
}
// 该函数创建path所指文件或目录的fd
// 并且创建空闲数据块
// flag为2代表创建目录,1为代表创建文件
int create_file_dir(const char *path, int flag)
{ // 获得父目录和目录名
printf("create_file_dir函数被调用!\n");
char *fname = NULL, *parent_path = NULL;
get_parent_and_fname(path, &parent_path, &fname);
// 根据父目录来获取父目录的inode和fd
struct file_directory *tfd = malloc(sizeof(struct file_directory));
struct inode *tinode = malloc(sizeof(struct inode));
int tmp1 = get_info_by_path(parent_path, tinode, tfd);
// 首先判断路径是否超出了层级的限制或者没在inode里面找到对应的fd
if (tmp1 != 0)
{ // 超出了层级限制或者没在inode里面找到对应的fd
free(tfd);
free(tinode);
return tmp1;
}
printf("create_file_dir:创建成功前,父目录inode号为:%d,大小为%d\n", tinode->st_ino, tinode->st_size);
// 判断文件名是否过长
int tooLong = 0;
int name_len = strlen(fname); // 文件名长度
char *exp_name = strchr(fname, '.'); // 判断是否有拓展名
int exp_len = 0;
if (!exp_name)
{
// 没有拓展名
if (name_len > 8)
tooLong = 1;
}
else
{
// 有拓展名
if (exp_name - fname > 8)
tooLong = 1;
else
{
exp_name++;
exp_len = strlen(exp_name);
if (exp_len > 3)
tooLong = 1;
}
}
if (tooLong)
{
printf("文件名:%s 或拓展名过长!文件名长度为:%d,拓展名长度为:%d超出规定字节!函数终止", fname, name_len, exp_len);
return -ENAMETOOLONG;
}
// 要对此时父目录的inode判断,是否为一个目录而不是文件
if (determineFileType(tinode) != 1)
{
// 不为目录
free(tfd);
free(tinode);
return -ENOENT;
}
// 调用函数,对父目录的inode中加入新创建的目录
int res = create_new_fd_to_inode(tinode, fname, flag);
if (res == 0)
{
printf("create_file_dir:创建成功后,父目录inode号为:%d,大小为%d\n", tinode->st_ino, tinode->st_size);
}
else
{
printf("create_file_dir:创建失败,错误代码为:%d\n", res);
}
// 创建操作后返回
free(tfd);
free(tinode);
return res;
}
// 该函数根据给定的inode,删除对应名称的目录
// 由rmdir和unlink同时调用
int remove_file_dir(struct inode *ind, const char *filename, int flag)
{
struct inode *tind = malloc(sizeof(struct inode));
struct file_directory *tfd = malloc(sizeof(struct file_directory));
// 要找到父目录inode里面,对应名称的目录在inode的第几个块,在那个块里面的偏移量
int blk_no = 0;
int offsize = 0;
// 首先判断删除的目录项是否存在
if (!is_inode_exists_fd_by_fname(ind, filename, tfd, &blk_no, &offsize))
{
free(tfd);
free(tind);
return -ENOENT;
}
// 获取要删除的文件的indoe
read_inode_by_fd(tind, tfd);
// 判断要删除文件的类型
// 此处由于该函数被多个函数调用,所以做一个if分支
if (flag == 2)
{
// 由rmdir调用
// 判断空目录和是否是目录
if (tind->st_size != 0)
{
// 不是空目录
free(tind);
free(tfd);
return -ENOTEMPTY;
}
// 判断是否是目录
if (determineFileType(tind) != 1)
{
// 不是目录
free(tind);
free(tfd);
return -ENOTDIR;
}
}
else
{
// 由unlink调用
// 判断是否为文件
if (determineFileType(tind) != 2)
{
// 不是文件(是目录)
free(tind);
free(tfd);
return -EISDIR;
}
}
// 在父目录inode,删除在其中的目录项信息
int end_blk = ind->st_size / MAX_DATA_IN_BLOCK; // 最后一块的块号
int end_offsize = ((ind->st_size - 1) % MAX_DATA_IN_BLOCK) - sizeof(struct file_directory) + 1;
end_offsize /= sizeof(struct file_directory); // 最后一个fd在最后一块的偏移量
// 对父目录inode里面进行相关信息的删除
// 设置一个临时的块进行读取后修改写入
struct data_block *tblk = malloc(sizeof(struct data_block));
// 读取最后一个块
read_block_by_ind_and_indNo(ind, end_blk, tblk);
// 利用偏移量读取最后一个块里面的最后一个fd
memcpy(tfd, (struct file_directory *)tblk + end_offsize, sizeof(struct file_directory));
// 读取要删除的fd项所在的块
read_block_by_ind_and_indNo(ind, blk_no, tblk);
// 将末尾的数据块覆盖原有的空目录
memcpy((struct file_directory *)tblk + offsize, tfd, sizeof(struct file_directory));
// 再把该数据块写回
write_block_by_ind_and_indNo(ind, blk_no, tblk);
// 空目录被覆盖(删除)后,释放其对应的ind与占用的数据块
clear_inode_map_by_no(tind->st_ino);
free(tind);
// 修改父目录inode的大小信息(少了16B)
ind->st_size -= sizeof(struct file_directory);
// 再把父目录的ind写回
write_inode_by_no(ind, ind->st_ino);
// 释放申请的内存
free(tblk);
free(tfd);
return 0;
}
// 该函数用于往已知的inode里面添加新建的目录项或文件
int create_new_fd_to_inode(struct inode *ind, const char *fname, int flag)
{
// 根据要求,不能在根目录下创建
// 首先要判断创建的文件是否处于根目录下
// 根据传入的inode的号是否为0来判断
if (ind->st_ino == 0 && flag == 1)
{
// 创建的文件在根目录下,不符合要求,直接返回
printf("create_new_fd_to_inode:创建的文件: %s 在根目录下,不符合要求", fname);
return -EPERM;
}
// 为新建的目录项分配空间
struct file_directory *fd = malloc(sizeof(struct file_directory));
// TODO:对是否存在目录项进行判断
if (is_inode_exists_fd_by_fname(ind, fname, fd, NULL, NULL))
{ // printf("存在!\n");
free(fd);
return -EEXIST;
}
// 对该inode中添加目录find_ino_dir
struct inode *cr_ind = malloc(sizeof(struct inode));
struct data_block *cr_blk = malloc(sizeof(struct data_block));
// 设置创建的目录的inode信息
// 由于此函数被mkdir和mknod同时调用
// 所以要进行if判断
if (flag == 2)
{
// 创建的是目录文件
// 目录通常需要具有可读(列出目录内容)和可执行(能进入目录)权限
// 典型的目录权限是755,表示用户可读写执行,组和其他用户只读执行
cr_ind->st_mode = (0755 | S_IFDIR);
}
else if (flag == 1)
{
// 创建的是文件
// 典型的文件权限通常是644,表示用户可读写,组和其他用户只读
cr_ind->st_mode = (0644 | S_IFREG);
}
// 下面为新创建的inode信息进行初始化
cr_ind->st_ino = assign_inode(); // 分配新的inode号
cr_ind->addr[0] = assign_block(); // 分配新的数据块
cr_ind->st_size = 0;
// 设置目录项信息
char *tname = strdup(fname); // 文件名
char *text = strchr(tname, '.'); // 拓展名的分隔符的指针
if (text == NULL)
{
// printf("准备创建文件:%s\n", tname);
// 没有拓展名
// 直接创建
create_dir_by_ino_number(tname, "", cr_ind->st_ino, "", fd);
// printf("create_new_fd_to_inode:创建的fd不存在拓展名:%s\n", fd->fext);
}
else
{
// printf("准备创建文件:%s\n", tname);
// 有拓展名,需要分割一下文件名和拓展名
*text = '\0'; // 分割
text++; // 移动到拓展名位置
create_dir_by_ino_number(tname, text, cr_ind->st_ino, "", fd);
// printf("create_new_fd_to_inode:创建的fd存在拓展名:%s\n", fd->fext);
}
fd->st_ino = cr_ind->st_ino;
// printf("创建fd成功,名为:%s,inode号为:%d\n", fd->fname, fd->st_ino);
// 寻找父目录对应的数据块的末尾块块号(ind相对块号)
short int end_blk = ind->st_size / MAX_DATA_IN_BLOCK;
// printf("要放入的快号为%d\n", end_blk);
// 首先判断父目录的末尾数据块是否装满