Assignment 7 FAQ, Errata, and Addenda

Due Wednesday, April 22, 2015 11:00 pm via sakai

Last update: Monday, March 23, 2015 13:57 EDT

I tried compiling hello.c on iLab machines with:


gcc -Wall -D_FILE_OFFSET_BITS=64 hello.c 'pkg-config fuse --cflags -libs' -o hello

and it says:


gcc: pkg-config fuse --cflags --libs: No such file or directory

This is with the line from the top of the hello.c file. I just tried compiling with what the TA gave me and it works on thee iLab machines:


cc -o hello hello.c -D_FILE_OFFSET_BITS=64 –lfuse

However, I am having issues with it working on my Ubuntu virtual machine. I am compiling hello.c the same way as I got it to work on the ILabs. The error message it displays is:


hello.c:13:18: fatal error: fuse.h: No such file or directory
 #include 
                  ^
compilation terminated.

On your ubuntu system, make sure you have the fuse package installed. I believe you’ll need to install two packages:


	apt-get install libfuse2
	apt-get install fuse
	apt-get install libfuse-dev

You’ll also need:


	apt-get install pkg-config

libfuse-dev is the development package. It installs, among other things, fuse.h.

I added printf statements to my code. However, none of the functions print anything. What's going on?

Printing only works if the fuse-based program runs in debug mode. Run the program in a separate window with the debug option on (which also makes the fuse process run in the foreground and hence easier to kill):


	./hello hellomnt -d

Then, in another window, try accessing things in hellomnt. You should see your debugging messages in the first window.

As I understand it, we have a fixed number of directories (e.g add, div, factor, etc) and in each of these directories we have doc file, right? Then I understand we should create directories for our calls: e.g., calling math/add/5/3 would create directories 5 and 3, and in directory 3 we will have some hidden text file with the result of the addition (8). Is it correct?

You never create directories or files: it's all an illusion presented by your program. When presented with a pathname, you’ll need to just test if it is a valid syntax. For example, if you see "/div", you need to identify that as a directory. If you see "/div/number", that will also be identified as a directory. "/div/number/number" will be identified as a regular file since it is a valid leaf of the directory tree and will contain the result of the first number divided by the second number. Anything else under "/div" aside from "/div/doc" will not be valid and will be reported as not existing.

This means that your getattr() function will set the appropriate mode for the incoming path:


stbuf-→st_mode = S_IFDIR | 0755;

or


stbuf-→st_mode = S_IFREG | 0444;

or


return -ENOENT;  // return a “no entry” error code

The big task in your getattr function is to parse the pathname and report the results. If you decide that you have a valid file name (e.g., "/add/123.45/6.789"), then you will also work out the results so you can return the slze of the results.

Note that you will use similar logic in your open() function: return 0 if the name is valid as a file you can open, otherwise -EACCESS.

You will also use the logic in your read() function: check that the path represents a valid syntax for a math result and return the requested substring of the results.

You say that we should create a tree structure where the root will be our mount point and "add", "div", "factor", etc. will be on a second level in a tree and so on up to the text files with math operation results? Is this right?

You don’t need to create an actual tree structure in your code. You need to be able to answer queries about the directory structure, though. In my case, for example, I just used a table that contained patterns to identify numbers. For example: "/div" is found in the table and identified as a directory, "/div/#" matches "/div/" followed by a number and identifies that as a directory as well. The pattern "/div/#/#" matches "/div/" followed by a number, followed by a "/", and followed by another number. A successful match identifies the path as a file. There’s a little more to it than that since I also identify the function that gets called to evaluate the result but you should get the basic idea. You can also just have a bunch of if statements to parse the path. There are many ways you can implement this.

Incidentally, you can use the strtod to convert a string to a double.

I read tutorial, wiki pages, and hello example program, but I am still confused how we create directories and files. I cannot understand how in the example hello.c we got the directory hello in the mount point's root. Do you actually create directories or we simply add them to our data-structure and then show to user (as I understand it is done with the filler function)?

You never actually create directories and files. Everything under the mount point is parsed by your program. In the case of the hello example, you present the user with a single file called "hello". It is defined in hello_path and its contents are defined in hello_str. There are no directories in this example except for the root of your file system. The entire file system contains only two valid names:

  1. "/" - root of the file system
  2. "/hello" - the single file (defined in hello_path)

Take a look at hello_getattr() to see how the incoming path is parsed to accept only these two files.

In hello_readdir(), we show the contents of the “directory” at the root level, which contains "." (itself), ".." (parent, which is the same as itself at the root)), and "hello" (the one file that we implement).

In hello_open() and hello_read(), we reject any path other than the one file that we can open ("/hello").

The user, these path names are the subset of the name under wherever the hello program is mounted. If you create a directory


	mkdir abc

and mount your file system under abc:


	./hello abc

then you would refer to the files under abc:


	cat abc/hello

while the program only gets the subset of the path that it cares about ("/hello").

Why do we need:


	(void) offset;
	(void) fi;

in readdir? I read about it but it is still confusing. The hello.c example does not use it, but the code from the tutorial has it.

You can get away with not using it. The offset is used in cases where you don't return the entire directory contents at once. The offset contains the byte offset to read the next chunk of the directory contents. In your case, you should return the complete directory contents whenever requested. If you put a debug statement in, you will see that the value of offset is 0 and you never use fi. At the top level ("/"), you return


	.
	..
	add
	sub
	mul
	div
	etc...

At levels right under each function-named directory (e.g., under "/add"), you return


	.
	..
	doc

At other levels (e.g., under "/add/12345"), you return only


	.
	..

If the pathname is not valid (e.g. "/foo" or "/add/123/456/678") then you return -ENOENT.

Important:: run in debugging mode!

To preserve your sanity, always run your file system in debug mode (at least until you get it working perfectly). That is, never run:


	./mathfs mount_point
Instead, run:

	./mathfs mount_point -d

If you don't do this, you risk forgetting to kill the process. You also won't see any of your debugging messages. Run the file system in debug mode in one window and enter your commands in another window.

So I installed FUSE successfully, but when I try to run "make install" with FUSE it gives me an error, what do I do in this instance?

Try changing the makefile to compile with:


cc -D_FILE_OFFSET_BITS=64 -o mathfs mathfs.c -lfuse -lm

I'm having difficulty with setting the file/dir creation time. I checked the document stating that the stat* st structure has three timestamp-related fields, st_atime, st_mtime, st_ctime. I tried to call built-in function to generate a timestamp to assign to each of them but, unfortunately, that did not work. I observed the results from the demo that the creation time is always the current time, therefore I think there should be a field to hold the timestamp each time the getattr API is called.

Is there any filed that I missed or my approach was wrong? Please guide me in the right direction.

I just made a more or less random decision to set the timestamp to the current time. It seemed appropriate for dynamic files whose results are generated on the fly. I set the timestamps in the getattr function via:


stbuf->st_mtime = stbuf->st_atime = stbuf->st_ctime = time(0);

Should we show the ".." directory in our mount point? I am not sure because it is treated as a root of our mounted filesystem, but we still should be able to do


cd ..

So I guess it should be present, right?

Yes, .. should be present. If you take a look at a Linux system you’ll see there’s a .. in the root directory. It's there just for consistency and refers to the same inode as the current directory. You should do:


	filler(buf, ".", NULL, 0);
	filler(buf, “..", NULL, 0);

What should we do when user enters smth like


ls math/add/1

Is it treated as a regular file or a directory? Should it be treated as valid or not? I guess I should just return -ENOENT right?

The getattr function will return -ENOENT only if the path is not valid. In the example of a path “/add/1”, you have a valid path and getattr should return a directory attribute:


	stbuf->st_mode = S_IFDIR | 0755;
	stbuf->st_nlink = 3;
	stbuf->st_size = 0;

If the path was was “/add/1/2” then you have a valid file name and should return a file attribute:


	stbuf->st_mode = S_IFREG | 0444;
	stbuf->st_nlink = 1;
	stbuf->st_size = strlen(results);

In the open function, however, you should disallow the opening of a directory. If the path is "/add/1", a call to open() should return -EACCESS.

How do I run this on a Mac?

First, install the osfuse package from https://osxfuse.github.io/.

You’ll need to set a few things in your compile line:

  • Add the following symbol definitions: -D_FILE_OFFSET_BITS=64 -D_DARWIN_USE_64_BIT_INODE
  • Define /usr/local as a prefix --prefix=/usr/local
  • Define the /usr/local/lib as the path to look for the dynamic library:

    
    -L/usr/local/lib 
    
  • Specify the include path so you can use #include :

    
    -I/usr/local/include/osxfuse
    
  • Link with

    
    	-losxfuse
    

    instead of

    
    	-lfuse
    

Your C compilation line will look like this:


cc -D_FILE_OFFSET_BITS=64 -D_DARWIN_USE_64_BIT_INODE --prefix=/usr/local -L/usr/local/lib -I/usr/local/include/osxfuse -o hello hello.c -losxfuse

When you run your code, you’ll probably see attempts to look up paths such as "/._." You can ignore these safely. Make sure your code runs on Linux too. I was able to run my code on my mac without changing any of it from the version I had for Linux.

When I tried to unmount on my Mac using the command "fusermount -u mount_point", I got an error saying command not found. Is the command different on Macs versus linux?
Yes. You should be able to use the standard umount command.
In the FAQ there is a question/answer about timestamps. Why do we need to set them? I do not see timestamp in hello.c example. I set all of them to time(0) as you said in the FAQ.
Mostly to give users comfort when they run ls -l. Since users don’t create files in this file system, they won’t be doing meaningful time-based comparisons on the files. If you set the time stamp to 0, users will see a date sometime in 1969, which looks odd.

How do I detect overflow if the user types something like:


cat math/exp/1000000/100000000000
Check the man page for pow(). If an overflow occurs, you’ll get either HUGE_VALF. However, if you just pass it to printf(), it will print “inf” (infinity). For the fibonacci sequence, you can can use unsigned integers and detect overflow when you see the next number in the sequence is smaller than the previous one.
If the user enters math/fib/1.2 would it be okay to say that file does not exists? Because my program flags it as invalid input and sends ENOENT.
Sure. That’s fine. I give the illusion that it’s a file that contains an error message but ENOENT is perfectly fine.
When testing the outcomes from my Fibonacci Sequence function, I observed that your demo version excludes the first Fibonacci Number, compared with my version. However, by the mathematical definition, Fibonacci Sequence starts with 0 as f0, then 1 as f1..., so which approach should I take to make the function correct? Please correct me if I was wrong.
The fibonacci sequence, according to the wikipedia article, can start with either {1, 1, 2, ...} or with {0, 1, 1, 2 ...}. I started mine with {1, 1} but the wikipedia article points out that the modern usage is to start it with {0, 1, 1, ...} so I updated my demo program to start it with 0.

I found a minor bug in the Fibonacci function in the demo version. The following steps would make the bug occur:

  1. cat math/fib/n (n could be any positive integer)
  2. cat math/fib/0

It seems that the cached data hasn't been flushed, so that calling fib function with parameter 0 gives the same outcomes.

You are correct about the bug. I neglected to check that the requested count was < 0 rather than ≤ 0. That’s also fixed in the updated demo program.
After completing this project, the structure of the codes gets a little bit messy, not graceful, as many function properties, definitions and an entry-holding struct are all in one single .c file. The submission part of the project instruction doesn't mention any other files could be submitted. I don't want to run the risk of losing points for that. Do you think I could move them to a separated pair of .c and .h file, only remaining those four FUSE functions in the main file to keep the structure clean and beautiful?
You’re welcome to break your file up into multiple files. Just provide a makefile to make it easy to compile.
I get compile errors when I use the pow function.
Use the -lm flag on the compile line to specify use of the math libary. See the man page for pow: man 3 pow.
What does "Transport endpoint not connected" mean? How do I fix it?

It means that your program (that implements the file system) crashed and did not get to unmount the file system. If you were running the file system with debugging enabled (which you should have been), you’d see a segmentation fault or some similar error in that window.

You need to manually unmount the file system. Run:


fusermount -u mount_point

replacing mount_point with whatever the directory name was that you used to mount the file system.

How come all my files show up as directories instead of regular files?

The file shows up as a directory if the mode in the inode states it is a directory. This is defined by the bit represented by the symbol S_IFDIR. The file shows up as a regular file if the mode in the inode states it is a regular file. This is defined by the bit represented by the symbol S_IFREG. The low-order bits of the mode value contain access permission bits. These are arranged in groups of 3 bits:


	ooogggwww
  • ooo: owner permissions: read, write, execute
  • ggg: group permissions: read, write, execute
  • www: world (everyone else) permissions: read, write, execute

Because they’re in groups of three, it is common to use octal values to refer to them since three bits make up an octal digit. Setting


	stbuf->st_mode = S_IFREG | 0644;

means that you’re setting the mode of the file to a regular file and the mode bits to 110100100 (0644):

  • 110 = read, write, execute access for the owner
  • 100 = read access for the group
  • 100 = read access for everyone else

Setting


	stbuf->st_mode = S_IFDIR | 0755;

means that you’re setting the mode of the file to a directory and the mode bits to 111101101 (0755). For directories, the mode bits refer to read access, create access, and search access, in that order:

  • 111 = read, create, and search for the owner
  • 101 = read and search for the group
  • 101 = read and search for everyone else
Can I have an extension?
Sorry, no. It would not be fair to give you an extension without giving one to everyone.