ImgFS: Image-oriented File System --- ImgFS format
The aim of this week is to:
- become fully acquainted with the framework and concepts of the project;
- implement the opening and closing of ImgFS files;
- then implement the simplest functionality, which lists the contents of the metadata.
So start by reading the main project description file to understand the general framework of the project. Once you've done that, you can continue below.
Provided material
In your group's GitHub repository, you will find the following files in provided/src/
:
imgfs.h
: function prototypes for the operations described here;imgfscmd.c
: the core of your "Filesystem Manager", the command line interface (CLI) to handleimgFS
; it reads a command and calls the corresponding functions to manipulate the database;imgfs_tools.c
: the tool functions forimgFS
; for example to display the data structure;imgfscmd_functions.h
andimgfs_cmd.c
: prototypes and definitions of the functions used by the CLI;util.h
andutil.c
: macros and miscellaneous functions; you do not need to use them (have a look to see if some may be useful);error.h
anderror.c
: error code and messages;- a
Makefile
containing useful rules and targets; - some unit and end-to-end tests in
provided/tests/{unit,end-to-end}/
; - data for your tests in
provided/tests/data
.
To avoid any trouble, the contents of the provided/
directory must never be modified!
Start by copying the files you need from provided/src/
into the done/
directory at the root of the project and registering it in git (git add
); for instance:
cp provided/src/*.h provided/src/Makefile provided/src/imgfs*.c provided/src/util.c provided/src/error.c done
git add done
You'll proceed similarly in the next weeks, whenever you'll need new files from provided/src
.
Tasks
The provided code does not compile; some work is still required, in the following steps (which are further detailed below):
- define the data structures required for
imgFS
; - parsing the command line arguments;
- define the functions
do_open()
anddo_close()
; - define the function
do_list()
; - define the function
do_list_cmd()
.
After reaching that point, the code should compile without errors. You will then have to test it.
An example usage of the CLI (the name of which is imgfscmd
) is:
./imgfscmd list empty.imgfs
where list
is a command provided to the CLI and empty.imgfs
is an argument for that command, here simply an ImgFS file (thus a file containing a whole filesystem).
0. STYLE!
Important Note: writing clean code, readable by everyone is very important. From experience, it seems that not everyone does this spontaneously at first ;-)
. There are tools that can help. For example, astyle
is a program designed to reformat source code to follow a standard (man astyle
for more details).
We provide you with a shortcuts (which uses astyle
): see the target style
in the provided Makefile (make style
to use it). We recommend you do a make style
before any (Git) commit.
1. Define data structures
The exact format of the header
and metadata
is given in the global project description. The types
struct imgfs_header
;struct img_metadata
;- and
struct imgfs_file
;
are to be defined in replacement of the "TODO WEEK 07: DEFINE YOUR STRUCTS HERE.
"" in imgfs.h
.
2. Parsing of the command line arguments
The second objective of this week is to process the arguments received from the command line. For modularization purposes, we will use function pointers.
To achieve this, the signatures of the functions do_COMMAND_cmd()
(and help()
) are uniform:
int do_COMMAND_cmd(int argc, char* argv[])
Those functions will handle the parsing of their respective additional arguments, while the main()
dispatches through them using the first CLI argument.
Using arrays for simplicity
To process all the different commands, we would like to avoid an "if-then-else" approach. Indeed, this would make adding new commands (which will arrive in the following weeks) more difficult, since it would require to add new cases for each of them. It would also make the code much less readable.
To avoid that, we put the various do_COMMAND_cmd()
(and help()
) functions in an array. We will take advantage of this to associate the names of the commands with their respective functions (e.g. the string "list"
with the do_list_cmd()
function), and then simply add a loop to the main()
function, to search for the received command among the list of possible commands -- for the moment, "list"
, "create"
, "help"
and "delete"
-- and call the corresponding function.
In imgfscmd.c
:
- define a
command
type, a pointer to functions such as those unified above; - define a
struct command_mapping
type containing a string (constant) and acommand
.
Then use these definitions to create an array named commands
associating the commands
"list", "create", "help", and "delete" to the corresponding functions.
Note: The "create"
, "help"
and "delete"
commands are not yet implemented, but you can already add them to the array.
Finally, complete the main()
using this array inside a loop. When the right command is found, simply call the function pointed to in the corresponding array entry, passing all the command line arguments.
For example, if you call the program
./imgfscmd list imgfs_file
then your code must call do_list_cmd()
with the following parameters: argc = 1
and argv = { "imgfs_file", NULL }
.
Your code must correctly handle the case where the command is not defined: in this case, simply call help()
and return ERR_INVALID_COMMAND
.
Your code can perfectly well assume that all commands in the commands
array are distinct.
3. do_open()
and do_close()
Now, we will implement the functions to open and close existing imgfs
files.
You need to write the definitions of do_open()
and do_close()
in the file imgfs_tools.c
.
The do_open()
function takes as arguments:
- the image base file name (
const char *
); - the file opening mode (
const char *
, e.g."rb"
,"rb+"
); - the
imgfs_file
structure in which to store read data.
The function must
- open the file;
- read the contents of the header;
- allocate the metadata array;
- read the contents of the metadata.
The function should return the value ERR_NONE
if all went well, and otherwise an appropriate error code in case of problems. You need to handle all possible error cases in this function, using the definitions in error.h
(see unit tests below).
Note: to check the validity of a pointer given as parameter, you can use the macro M_REQUIRE_NON_NULL(ptr)
, which will make the function return ERR_INVALID_ARGUMENT
if ptr == NULL
(see util.h
).
The do_close()
function takes a single argument of structure type imgfs_file
and must close the file and free the metadata array. It returns no value. Here too, remember to handle the possible error case: if the file (FILE*
) is NULL
. This should be a reflex when you're writing code, especially when you're using a pointer. We won't mention it again.
4. Define do_list()
Then create a new file imgfs_list.c
to implement the do_list()
function. If output_mode
is STDOUT
, the purpose of do_list()
is first to print the contents of the "header" using the supplied print_header()
tool function, and then to print (examples below)
-
either
<< empty imgFS >>
if the database does not contain any images;
-
or the metadata of all valid images (see
print_metadata()
, provided inimgfs.h
).
The case output_mode == JSON
will be implemented later in the project; you may just call TO_BE_IMPLEMENTED()
in this case (see util.h
).
Warning: there may be "holes" in the metadata array: one or more invalid images may exists between two valid ones.
5. Complete do_list_cmd()
In order to be able to use the do_list()
function from the command line, implement the do_list_cmd()
function in imgfscmd_functions.c
, which receives the command line arguments as parameters (as explained before).
The first element of the array is the name of the file containing the database. After checking that the parameters are correct, open the database and display its contents, using the above functions.
Examples and tests
To make it easier to understand the various functions described above, a few examples are given here. These examples are in the provided tests (see below).
Black-box (end to end) testing
It's best to start testing your code on simple cases that you're familiar with.
You can test your code with the supplied .imgfs
files: the command
./imgfscmd list ../provided/tests/data/empty.imgfs
should display (exact file here):
*****************************************
********** IMGFS HEADER START ***********
TYPE: EPFL ImgFS 2024
VERSION: 0
IMAGE COUNT: 0 MAX IMAGES: 10
THUMBNAIL: 64 x 64 SMALL: 256 x 256
*********** IMGFS HEADER END ************
*****************************************
<< empty imgFS >>
while
./imgfscmd list ../provided/tests/data/test02.imgfs
should display (exact file here) :
*****************************************
********** IMGFS HEADER START ***********
TYPE: EPFL ImgFS 2024
VERSION: 2
IMAGE COUNT: 2 MAX IMAGES: 100
THUMBNAIL: 64 x 64 SMALL: 256 x 256
*********** IMGFS HEADER END ************
*****************************************
IMAGE ID: pic1
SHA: 66ac648b32a8268ed0b350b184cfa04c00c6236af3a2aa4411c01518f6061af8
VALID: 1
UNUSED: 0
OFFSET ORIG.: 21664 SIZE ORIG.: 72876
OFFSET THUMB.: 0 SIZE THUMB.: 0
OFFSET SMALL: 0 SIZE SMALL: 0
ORIGINAL: 1200 x 800
*****************************************
IMAGE ID: pic2
SHA: 95962b09e0fc9716ee4c2a1cf173f9147758235360d7ac0a73dfa378858b8a10
VALID: 1
UNUSED: 0
OFFSET ORIG.: 94540 SIZE ORIG.: 98119
OFFSET THUMB.: 0 SIZE THUMB.: 0
OFFSET SMALL: 0 SIZE SMALL: 0
ORIGINAL: 1200 x 800
*****************************************
Note: you may compare your results by using:
./imgfscmd list ../provided/tests/data/test02.imgfs > mon_res_02.txt
diff -w ../provided/tests/data/list_out/test02.txt mon_res_02.txt
More details: man diff
.
Provided tests
setup
The provided test suites require several dependencies: Check and Robot Framework (and its own dependency, parse). On (your own) Ubuntu, you can install them with:
sudo apt install check pip pkg-config
then, depending on how you're used to work in Python, either as root or in your Python virtual environment (maybe to be created):
pip install parse robotframework
(Of course you'll have to run the tests in that Python venv, if that's your usual way to work with Python.)
ON EPFL VMs, you have to setup a personnal Python virtual environment.
If you already have one, activate it and install the two above mentioned packages (parse
and robotframework
).
It you don't, we recommand you create your personnal Python virtual environment in myfiles
:
cd ~/Desktop/myfile
python -m venv mypyvenv
cd mypyvenv
cp -r lib lib64 ## this fixes the first warning
cd ..
python -m venv mypyvenv
Ignore the (second) warnings.
Then activate it:
source mypyvenv/bin/activate
and then install the required packages:
pip install parse robotframework
And you're done.
The only thing you'll have to do next time you login and you want to run the "end to end" tests, is to activate your Python virtual environment:
source ~/Desktop/myfiles/mypyvenv/bin/activate
Of course, you can also add that to your ~/.bashrc
!
provided tests
We provide you with a few tests to run against your code by using make check
, both unit tests (testing functions one by one) and end-to-end tests (testing the whole executable at once).
We strongly advise you to complete them by adding you own tests for edge cases; the imgFS
files are in provided/test/data
. You can check the unit tests in provided/test/unit
and the end-to-end ones in provided/test/end-to-end
to understand how to write your own.
Note: Don't forget to never push modifications in the provided/
directory; instead move the test/
directory to done/
and update the TEST_DIR
variable in the Makefile
accordingly.
We also provide a make feedback
(make feedback-VM-CO
if you're working on EPFL VMs) which gives partial feedback on your work. This is normally used for a minimal final check of your work, before handing it in. It's better to run local tests directly on your machine beforehand (including more tests you've added yourself, if necessary).
The Docker image used by make feedback
will be tagged latest
every week, but if you want to run feedback for a specific week, change (in the Makefile
at the line that defines IMAGE
) this latest
tag to weekNN
where NN
is the desired week number, e.g.:
IMAGE=chappeli/cs202-feedback:week07
Work organization
It's up to you to organize the group work as best you can, according to your objectives and constraints; but remember to divide the task properly between the two members of the group. If you haven't already read it in full, we recommend you read the end of the foreword page.
Submission
You don't have to formally deliver your work for this first week of the project, as the first deliverable will only be due at the end of the week 10 (deadline: Sunday May 5th, 23:59), together with weeks 8 and 9 work.
Having said that, we strongly advise you to mark with a commit when you think you've completed some part of the work and especially once you reached the end of this week (you can do other commits beforehand, of course!):
-
add the new
imgfs_list.c
file to thedone/
directory (of your group GitHub repository; i.e. corresponding to the project), along with your own tests if required:git add imgfs_list.c
-
also add the modified files (but NOT the
.o
, nor the executables!):imgfs_tools.c
,imgfs.h
and maybeMakefile
:git add -u
-
check that everything is ok:
git status
or
git status -uno
to hide unwanted files, but be careful to not hide any required file!
-
create the commit:
git commit -m "final version week07"
In fact, we strongly advise you to systematically make these regular commits, at least weekly, when your work is up and running. This will help you save your work and measure your progress.