I just started playing with xv6, re-implementation of Unix version 6. It can easily build and run.
I thought implementing some command is a good practice, so I decided to add simple one, pwd
. It can be made in pure user land, and no additional system call required.
Extract file information on xv6
Referring to ls.c, you can extract file information (struct stat
) using stat
function.
You can also enumerate directory entries calling open
function with directory path, read
contents as struct dirent
.
Find absolute path
I am not familiar with concept of Unix, so looking for the code, and it seems struct inode
(and its inum
, inode number) is the key part for the file system. But interestingly, it doesn't have its file name. To get file name, you have to enumerate its directory and search for inum
.
To get absolute path, you have to get file name recursively, until reach to the root.
Code
Here is the main
function:
#include "types.h"
#include "fcntl.h"
#include "fs.h"
#include "stat.h"
#include "user.h"
#define NULL ((void*)0)
#define FALSE (0)
#define TRUE (1)
#define PATH_SEPARATOR "/"
static int getcwd(char* resultPath);
static char* goUp(int ino, char* ancestorPath, char* resultPath);
static int dirlookup(int fd, int ino, char* p);
int main(int argc, char *argv[]) {
char resultPath[512];
if (getcwd(resultPath))
printf(1, "%s\n", resultPath);
else
printf(2, "pwd failed");
exit();
}
getcwd
function returns current working directory to the given buffer:
static int getcwd(char* resultPath) {
resultPath[0] = '\0';
char ancestorPath[512];
strcpy(ancestorPath, ".");
struct stat st;
if (stat(ancestorPath, &st) < 0)
return FALSE;
char* p = goUp(st.ino, ancestorPath, resultPath);
if (p == NULL)
return FALSE;
if (resultPath[0] == '\0')
strcpy(resultPath, PATH_SEPARATOR);
return TRUE;
}
ancestorPath
holds relative path to ancestors. It becomes "."
, "./.."
, "./../.."
, ... and is used to pass to stat
function.
goUp
function goes to parent directory for ancestorPath
, and find the absolute path, recursively:
static char* goUp(int ino, char* ancestorPath, char* resultPath) {
strcpy(ancestorPath + strlen(ancestorPath), PATH_SEPARATOR "..");
struct stat st;
if (stat(ancestorPath, &st) < 0)
return NULL;
if (st.ino == ino) {
// No parent directory exists: must be the root.
return resultPath;
}
char* foundPath = NULL;
int fd = open(ancestorPath, O_RDONLY);
if (fd >= 0) {
char* p = goUp(st.ino, ancestorPath, resultPath);
if (p != NULL) {
strcpy(p, PATH_SEPARATOR);
p += sizeof(PATH_SEPARATOR) - 1;
// Find current directory.
if (dirlookup(fd, ino, p))
foundPath = p + strlen(p);
}
close(fd);
}
return foundPath;
}
Even if you are at the root directory, you can get stat
for the parent direcotry without error (and you can cd ..
). It seems you have to detect you are at the root if parent's and current directory's ino
are same.
dirlookup
function finds file name for the given inode number from its parent directory:
// @param fd file descriptor for a directory.
// @param ino target inode number.
// @param p [out] file name (part of absPath), overwritten by the file name of the ino.
static int dirlookup(int fd, int ino, char* p) {
struct dirent de;
while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0)
continue;
if (de.inum == ino) {
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = '\0';
return TRUE;
}
}
return FALSE;
}
Build and add to file image
You can easily add your extra command to image file on xv6 . Modify UPROGS in Makefile with naming convention, add _pwd
to it.
Test
$ pwd
/
$ mkdir foo
$ mkdir foo/bar
$ cd foo/bar
$ /pwd
/foo/bar
I stumbled using pwd
at /foo/bar
. It seems xv6 doesn't have environment variables, and search path in a shell:
$ pwd # <= without '/' causes exec error, because cannot find the command.
exec: fail
exec pwd failed
You have to add /
to point exact command path on xv6.
Top comments (0)