How to tell fuse3 that you need a device and a mount point in command-line arguments - filesystems

I'm using fuse3 to write a file system driver that should parse all the fuse options and then take two arguments, a device path (or file containing a file system image) and a mount-point.
Is there a convenient way to use fuse's command-line parsing to extract the device from the command line? I've taken to manipulating the arguments before I hand them to fuse like this:
struct fuse_args args;
const char *device = NULL;
memset(&args, 0, sizeof(args));
for (int i = 0; i < argc; ++i)
if (i > 0 && i == argc - 2 && *argv[i] != '-')
image = argv[i];
else
fuse_opt_add_arg(&args, argv[i]);
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (!device) {
usage(argv[0]);
fuse_opt_add_arg(&args, "--help");
}
fuse_main(args.argc, args.argv, &oper, nullptr);
But this is totally gross and doesn't quite work if the user specified only one argument, unless that argument also happens to be a valid mountpoint, because fuse seems to check the mountpoint viability before printing the help.
Surely this must be a common thing to want to do, so I'm wondering what the correct idiom is for such a file system.

so for this you will have to restrict the user on the order of the cmd arguments(all fuse/mount options come before the devicepath and mountpoint). To make it simple, ensure that the path to the device and mount point are provided last:
So in your main function, this statement will check that there is a correct number of arguments and the mount point and device do not start with hyphens as with options.
if ((argc < 3) || (argv[argc-2][0] == '-') || (argv[argc-1][0] == '-'))
{
fprintf(stderr, "usage: ./myfuse [FUSE and mount options] devicepath mountPoint\n");
abort();
}
Then extract the device path and store it in a struct:
struct my_state *my_data;
my_data = malloc(sizeof(struct mi_state));
if (mi_data == NULL) {
perror("main calloc");
abort();
}
my_data->devicepath = realpath(argv[argc - 2], NULL);
argv[argc-2] = argv[argc-1];
argv[argc-1] = NULL;
argc--;
Notice that I remove the device paths from argv and decrement argc by 1 before passing hem to the fuse_main function
Then while calling the fuse_main function make sure to pass the my_data struct:
fuse_main(argc, argv, &my_fuse_operations, mi_data);
Here is the defination of the my_state struct which you can put in a header file:
struct my_state{
char *devicepath;
};
You should also add this definition in the header file below the struct definition:
#define BB_DATA ((struct mi_state *) fuse_get_context()->private_data)
And also in your init function call fuse_get_context and return BB_DATA:
void *my_init()
{
fuse_get_context();
return BB_DATA;
}
The return value will be passed in the private_data field of fuse_context to all file operations and as a parameter to the destroy() method.
The fuse_context is set up before this function is called, and fuse_get_context()->private_data returns the user_data passed to fuse_main().

Related

`execvp()` seems to not be completing the path search

TL;DR -- what sorts of things might cause an execvp call to not fully function/ search the path properly?
I'm on the tail end of building a rudimentary shell with some quality of life features that I've added over time e.g. history, alias's, and completions. I built those features on top of a functional shell that had a working $PATH search for execution e.g. typing in "ls -la" produced the desired behavior. As you might imagine, I accomplished this just using execvp. (This is written in C if it's not already clear)
I have not changed any of my tokenizing logic and have ensured that the file name is correct; in particular, execvp was producing the desired behavior before I had added these features to my REPL. echo "hello" still produces a tokenized char **xyz and the first token is indeed echo, null-terminated, and so on. That is, my call still looks like, with variables filled-in, ... execvp("echo", argv); after which I call perror, which should only trigger when something has gone awry. Each time I just run the above command, though, since I've added in these features, it returns a failure with the no such file or directory --- before I added these features in though, the behavior was as desired. I'll note, though, that running /bin/echo "hello" runs as expected. Examples are WLOG.
I'm not sure where I should even start looking for errors, and my Google-fu has been mostly fruitless: any suggestions?
I'm initially going to omit code because it totals to several hundred lines and a MWE would not be particularly minimal in addition to my desires to keep this general rather than very particular to my code, though I'm not sure what's causing this. My repository is public and up-to-date, and I'm happy to post any code here.
EDIT:
I knew I wasn't explicitly editing the PATH variable, etc., but this block of code was the problem:
// Grab $PATH from env
char *pathvar = getenv("PATH");
if (pathvar) {
char *path;
int i;
// tokenize on colon to get paths
// then use that immediately to
// scandir, and add everything in
// there to the completions system
path = strtok(pathvar, ":");
while (path) {
struct dirent **fListTemp;
int num_files = scandir(path, &fListTemp, NULL, alphasort);
// only adding the names that are completely composed of
// lower case letters; completions are done using a naive
// Trie Node structure that only supports lowercase letters
// for now... e.g. g++ does not work, and the '+' leads to
// a seg-fault. Same holds for . and ..
for (i = 0; i < num_files; i++) {
char *curr = fListTemp[i]->d_name;
if (strcmp(curr, ".")==0 || strcmp(curr, "..")==0){
continue;
} else if (notalpha(curr)) {
continue;
} else {
str_tolower(curr);
tn_insert(completions, curr);
}
}
for (i = 0; i < num_files; i++) {
free(fListTemp[i]);
}
free (fListTemp);
path = strtok(NULL, ":");
}
} else {
fprintf(stderr, "{wsh # init} -- $PATH variable could not be found?");
}
Note that
The getenv() function returns a pointer to the value in the
environment, or NULL if there is no match.
so my original code was indeed tampering with the PATH variable. The solution I came up with quickly was just to create a copy of that string and use that to parse through the PATH instead:
// Grab $PATH from env
char *pathvar = getenv("PATH");
char *pathvar_cpy = strcpy(pathvar_cpy, pathvar);
if (pathvar_cpy) {
char *path;
int i;
path = strtok(pathvar_cpy, ":");
while (path) {
// Scan directory
struct dirent **fListTemp;
int num_files = scandir(path, &fListTemp, NULL, alphasort);
for (i = 0; i < num_files; i++) {
char *curr = fListTemp[i]->d_name;
if (strcmp(curr, ".")==0 || strcmp(curr, "..")==0){
continue;
} else if (notalpha(curr)) {
continue;
} else {
str_tolower(curr);
tn_insert(completions, curr);
}
}
for (i = 0; i < num_files; i++) {
free(fListTemp[i]);
}
free (fListTemp);
path = strtok(NULL, ":");
}
} else {
fprintf(stderr, "{wsh # init} -- $PATH variable could not be found?");
}

Find pathname from dlopen handle on OSX

I have dlopen()'ed a library, and I want to invert back from the handle it passes to me to the full pathname of shared library. On Linux and friends, I know that I can use dlinfo() to get the linkmap and iterate through those structures, but I can't seem to find an analogue on OSX. The closest thing I can do is to either:
Use dyld_image_count() and dyld_get_image_name(), iterate over all the currently opened libraries and hope I can guess which one corresponds to my handle
Somehow find a symbol that lives inside of the handle I have, and pass that to dladdr().
If I have apriori knowledge as to a symbol name inside of the library I just opened, I can dlsym() that and then use dladdr(). That works fine. But in the general case where I have no idea what is inside this shared library, I would need to be able to enumerate symbols to do that, which I don't know how to do either.
So any tips on how to lookup the pathname of a library from its dlopen handle would be very much appreciated. Thanks!
Here is how you can get the absolute path of a handle returned by dlopen.
In order to get the absolute path, you need to call the dladdr function and retrieve the Dl_info.dli_fname field.
In order to call the dladdr function, you need to give it an address.
In order to get an address given a handle, you have to call the dlsym function with a symbol.
In order to get a symbol out of a loaded library, you have to parse the library to find its symbol table and iterate over the symbols. You need to find an external symbol because dlsym only searches for external symbols.
Put it all together and you get this:
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <mach-o/nlist.h>
#import <stdio.h>
#import <string.h>
#ifdef __LP64__
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct nlist_64 nlist_t;
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct nlist nlist_t;
#endif
static const char * first_external_symbol_for_image(const mach_header_t *header)
{
Dl_info info;
if (dladdr(header, &info) == 0)
return NULL;
segment_command_t *seg_linkedit = NULL;
segment_command_t *seg_text = NULL;
struct symtab_command *symtab = NULL;
struct load_command *cmd = (struct load_command *)((intptr_t)header + sizeof(mach_header_t));
for (uint32_t i = 0; i < header->ncmds; i++, cmd = (struct load_command *)((intptr_t)cmd + cmd->cmdsize))
{
switch(cmd->cmd)
{
case LC_SEGMENT:
case LC_SEGMENT_64:
if (!strcmp(((segment_command_t *)cmd)->segname, SEG_TEXT))
seg_text = (segment_command_t *)cmd;
else if (!strcmp(((segment_command_t *)cmd)->segname, SEG_LINKEDIT))
seg_linkedit = (segment_command_t *)cmd;
break;
case LC_SYMTAB:
symtab = (struct symtab_command *)cmd;
break;
}
}
if ((seg_text == NULL) || (seg_linkedit == NULL) || (symtab == NULL))
return NULL;
intptr_t file_slide = ((intptr_t)seg_linkedit->vmaddr - (intptr_t)seg_text->vmaddr) - seg_linkedit->fileoff;
intptr_t strings = (intptr_t)header + (symtab->stroff + file_slide);
nlist_t *sym = (nlist_t *)((intptr_t)header + (symtab->symoff + file_slide));
for (uint32_t i = 0; i < symtab->nsyms; i++, sym++)
{
if ((sym->n_type & N_EXT) != N_EXT || !sym->n_value)
continue;
return (const char *)strings + sym->n_un.n_strx;
}
return NULL;
}
const char * pathname_for_handle(void *handle)
{
for (int32_t i = _dyld_image_count(); i >= 0 ; i--)
{
const char *first_symbol = first_external_symbol_for_image((const mach_header_t *)_dyld_get_image_header(i));
if (first_symbol && strlen(first_symbol) > 1)
{
handle = (void *)((intptr_t)handle | 1); // in order to trigger findExportedSymbol instead of findExportedSymbolInImageOrDependentImages. See `dlsym` implementation at http://opensource.apple.com/source/dyld/dyld-239.3/src/dyldAPIs.cpp
first_symbol++; // in order to remove the leading underscore
void *address = dlsym(handle, first_symbol);
Dl_info info;
if (dladdr(address, &info))
return info.dli_fname;
}
}
return NULL;
}
int main(int argc, const char * argv[])
{
void *libxml2 = dlopen("libxml2.dylib", RTLD_LAZY);
printf("libxml2 path: %s\n", pathname_for_handle(libxml2));
dlclose(libxml2);
return 0;
}
If you run this code, it will yield the expected result: libxml2 path: /usr/lib/libxml2.2.dylib
After about a year of using the solution provided by 0xced, we discovered an alternative method that is simpler and avoids one (rather rare) failure mode; specifically, because 0xced's code snippet iterates through each dylib currently loaded, finds the first exported symbol, attempts to resolve it in the dylib currently being sought, and returns positive if that symbol is found in that particular dylib, you can have false positives if the first exported symbol from an arbitrary library happens to be present inside of the dylib you're currently searching for.
My solution was to use _dyld_get_image_name(i) to get the absolute path of each image loaded, dlopen() that image, and compare the handle (after masking out any mode bits set by dlopen() due to usage of things like RTLD_FIRST) to ensure that this dylib is actually the same file as the handle passed into my function.
The complete function can be seen here, as a part of the Julia Language, with the relevant portion copied below:
// Iterate through all images currently in memory
for (int32_t i = _dyld_image_count(); i >= 0 ; i--) {
// dlopen() each image, check handle
const char *image_name = _dyld_get_image_name(i);
uv_lib_t *probe_lib = jl_load_dynamic_library(image_name, JL_RTLD_DEFAULT);
void *probe_handle = probe_lib->handle;
uv_dlclose(probe_lib);
// If the handle is the same as what was passed in (modulo mode bits), return this image name
if (((intptr_t)handle & (-4)) == ((intptr_t)probe_handle & (-4)))
return image_name;
}
Note that functions such as jl_load_dynamic_library() are wrappers around dlopen() that return libuv types, but the spirit of the code remains the same.

Pointers and String manipulation

I'm writing a function/method in C that is supposed to do the following:
/** * Recieves the list of arguments and copies to conffile the name
of * the configuration file. * Returns 1 if the default file name
was used or 2 if * the parameter -f existed and therefor the
specified name was used. Returns 0 if there is a -f * parameter
but is invalid. (last argument)
*/
Basically one of these two option will be "asked" by the program:
1- controller -f FCFS|LOOK
2- controller FCFS|LOOK
If the second is asked, we enter the case of using the default name.
int get_conf_name(char* argv[], char* conffile) {
// Searches for -f
s = strstr(*argv, "-f");
if(s != NULL){
if(//its valid){
strcpy(conffile, //the name of the file which comes after -f
return 2
}
else return 0
}
else{
strcpy(confile, "config.vss")
return 1
}
}
The problem here is how do I get the word after -f and copy it to confile? And, can I access argv the same way I access conffile, since one of them is an array?
I thought of using a for loop and a strlen, but that would be a lot of unecessary work for the computer wouldn't it?
I think the assignment expects you to go through the individual arguments one by one, comparing them to "-f".
If you see no -f, you know that the default file needs to be used
If you see the flag in the final position, you know that the -f is invalid
If you see the flag in the position k, then the file name will be in argv[k+1]
The skeleton of your program should look like this:
bool foundFlag = false;
for (int i = 1 ; i < argc ; i++) {
if (strcmp(argv[i], "-f") == 0) {
if (i == argc-1) {
// Error
} else {
// argv[i+1] is the file name;
}
foundFlag = true;
break;
}
}
if (!foundFlag) {
// Default name is used
}

Porting Unix to Windows- usage of pwd.h

I'm trying to compile libUnihan code with MinGW, but have run into a function which requires porting. The purpose of the function is to get a canonical path representation. It uses pwd.h (which is POSIX, and MinGW isn't) so it can account for the use of '~' to mean the home directory by retrieving a passwd struct, which contains pw_dir. I did find a little information here, and a port of realpath here, but I am still entirely at a loss as to how to deal with this. With MinGW, I still have a home directory represented by ~ and located at /home/nate, but since it isn't POSIX, I don't have pwd.h to help me find where this home directory is.
Q: How can I port the function below to work properly with MinGW?
/**
* Return the canonicalized absolute pathname.
*
* It works exactly the same with realpath(3), except this function can handle the path with ~,
* where realpath cannot.
*
* #param path The path to be resolved.
* #param resolved_path Buffer for holding the resolved_path.
* #return resolved path, NULL is the resolution is not sucessful.
*/
gchar*
truepath(const gchar *path, gchar *resolved_path){
gchar workingPath[PATH_MAX];
gchar fullPath[PATH_MAX];
gchar *result=NULL;
g_strlcpy(workingPath,path,PATH_MAX);
// printf("*** path=%s \n",path);
if ( workingPath[0] != '~' ){
result = realpath(workingPath, resolved_path);
}else{
gchar *firstSlash, *suffix, *homeDirStr;
struct passwd *pw;
// initialize variables
firstSlash = suffix = homeDirStr = NULL;
firstSlash = strchr(workingPath, DIRECTORY_SEPARATOR);
if (firstSlash == NULL)
suffix = "";
else
{
*firstSlash = 0; // so userName is null terminated
suffix = firstSlash + 1;
}
if (workingPath[1] == '\0')
pw = getpwuid( getuid() );
else
pw = getpwnam( &workingPath[1] );
if (pw != NULL)
homeDirStr = pw->pw_dir;
if (homeDirStr != NULL){
gint ret=g_sprintf(fullPath, "%s%c%s", homeDirStr, DIRECTORY_SEPARATOR, suffix);
if (ret>0){
result = realpath(fullPath, resolved_path);
}
}
}
return result;
}
The purpose is to implement ~[username]/ remapping logic. This sort of code makes sense in Linux/UNIX environments, but the most common use is just to refer to the user's own home directory.
For expediency, I'd just add support for the common case - ~/ - i.e. the current user, and not bother supporting the more general case - have it fail with an obvious error in that case.
The function to get the current user's home directory is SHGetFolderPath.
#include <windows.h>
char homeDirStr[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, homeDirStr))) {
// Do something with the path
} else {
// Do something else
}
In the case of a failed lookup of the user, the code you pasted does not try to replace that string, but simply returns NULL, so you could emulate that.

Pointer to Pointer

I am having a lot of trouble with this piece of code (I am not good at pointers :P). So here is the code.
printf("\n Enter the file name along with its extensions that you want to delete:-");
scanf("%s",fileName);
deletefile_1_arg=fileName;
printf("test\n");
result_5 = deletefile_1(&deletefile_1_arg, clnt);
if (result_5 == (int *) NULL) {
clnt_perror (clnt, "call failed");
}
else
{
printf("\n File is deleted sucessfully");
goto Menu2;
}
break;
Function that is getting called is as following.
int *
deletefile_1_svc(char **argp, struct svc_req *rqstp)
{
static int result;
printf("test2\n");
printf("%s",**argp);
if(remove(**argp));
{
printf("\nFile Has Been Deleted");
result=1;
}
return &result;
}
I am getting test2 on console but. It does not print value of argp / removes that perticular file. I am not sure what I am doing wrong. Please help me.
The argp is a pointer to a pointer char, and you are trying to use it as a pointer to char, try change your code to:
printf("%s", *argp);
You would also need to change your remove call to:
remove(*argp);
I always found drawing pictures helped understand pointers. Use boxes for memory addresses and a label for the box is the variable name. If the variable is a pointer, then the contents of the box is the address of another box (draw line to the other box).
You are using pointers when you don't need to. Your "deletefile1_svc" function doesn't manipulate the value of "argp" at all so it doesn't need a pointer-to-pointer. Plus your "result" doesn't need to be returned as a pointer since it is simply a numeric value. You also don't initialize result (it might be zero) or re-initialize it (it is static so it will remember the last value assigned to it).
int
deletefile_1_svc(const char *argp, struct svc_req *rqstp)
{
int result = 0; /* Initial value => failure */
if (remove (argp) == 0)
{
result = 1; /* 1 => success */
}
return result;
}
To call the function use:
result_5 = deletefile1_svc(filename, clnt);
if (result_5 == 0)
// Failed
else
// Success
That will make the code simpler and less prone to bugs.

Resources