Why Input/Output Error occurs when use ls command in my fuse filesystem? - linux

Why I/O Error causes when I tried ls . command in my fuse filesystem?
My filesystem has a limitation that it only allows mail address type as individual filename and it does not allows sub directory.
Now I want to display a list of file name when use ls . but it does not work.
I understood that it must implement a callback function. (Correspond function is ll_readdir in mycode)
but I have no idea what points are causes the errors.
Update:
Now I use strace command to investigate what system call raise a this error.
According to result of strace, this error caused in getdents64 syscall.
getdents64(3, 0x5611ed000540, 32768) = -1 EIO (Input/output error)
Code1 (implementation of mm:
struct mutex_map {
int counter = 2;
std::mutex _mtx;
std::unordered_map<int, std::string> _data;
std::unordered_map<std::string, int> _rev_data;
public:
int set_value(const char* value) {
std::string s = std::string(value);
std::lock_guard<std::mutex> lock(_mtx);
counter++;
_data[counter] = s;
_rev_data[s] = counter;
return counter;
}
const char* get_value(int key) { return _data[key].c_str(); }
int get_ino(const char* name) { return _rev_data[std::string(name)]; }
};
static mutex_map mm;
Code2: (sendmailfs_stat)
static int sendmailfs_stat(fuse_ino_t ino, struct stat* stbuf,
size_t name_length) {
uid_t uid = getuid();
gid_t gid = getgid();
stbuf->st_ino = ino;
if (ino == 1) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
stbuf->st_uid = uid;
stbuf->st_mode = S_IFDIR;
} else {
stbuf->st_mode = S_IFCHR | 0666;
stbuf->st_nlink = 1;
stbuf->st_size = name_length;
stbuf->st_uid = uid;
stbuf->st_gid = gid;
}
return 0;
}
Code 3: (implementation of readdir callback)
static void ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi) {
// printf("size_t=%ld, fh=%ld, ino=%ld\n", size, fi->fh, ino);
if (ino == 1) {
off_t o = 0;
size_t rest = size;
size_t res;
char* buf = (char*)calloc(1, size);
struct stat dotst;
sendmailfs_stat(ino, &dotst, strlen("."));
res = fuse_add_direntry(req, buf, rest, ".", &dotst, o);
rest -= res;
o++;
printf("%s\n", "start of loop");
uint64_t num_contain = 0;
for (auto& c : mm._data) {
const char* t = c.second.c_str();
int ino2 = mm.get_ino(t);
struct stat st;
sendmailfs_stat(ino2, &st, strlen(t));
fuse_entry_param e;
e.ino = ino2;
e.attr_timeout = 0;
sendmailfs_stat(ino2, &e.attr, strlen(t));
res = fuse_add_direntry_plus(req, buf, rest, t, &e, o);
o += 1;
rest -= res;
}
fuse_reply_buf(req, buf, size);
}
}

A bit late, but if anyone having this error stumbles upon this thread, they might want to check first whether the filesystem is mounted properly. The Input/output error from getdents64 is symptomatic of a filesystem that was unmounted, but failed for some reason (like a file was still in use when user tried the umount command), so still looks mounted, but no data can be fetched from it.
So in this case, some process could be calling umount (and failing) before you run ls, or the filesystem failed to correctly mount in the first place for some reason.

Related

Running a userspace process in user mode with Linux kernel call_usermodehelper

I tried to execute a userspace binary inside the Linux kernel module with call_usermodehelper. I found the launched application is running in root mode. Is it possible to run the application in a user mode, for example, named user1?
int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
// struct cred *new;
new = prepare_creds();
if (!new)
return -ENOMEM;
new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
return commit_creds(new);
}
static int init_func(struct subprocess_info *info, struct cred *new)
{
printk("[%d]\n", current->pid);
alter_uid_gid(1000, 1000, new);
return 0;
}
static int user_process_fork(void *data)
{
struct subprocess_info *sub_info;
int ret = 0;
char *path = (char *)data;
char *argv[] = {path, NULL};
static char *envp[] = {"HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
init_func, NULL, NULL);
if (sub_info == NULL) return -ENOMEM;
ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
pr_info("%s: ret %d\n", __func__, ret);
do_exit(0);
return ret;
}
Based on Milag's comment, I tried to update the u[g/e/s]idin the init_func() with prepare_creds() and commit_creds(new). In the kernel log, I can see current->uid has changed to 1000. But when I run ps aux, the process is still in root mode. Any idea why?
After I read some kernel sources and posted comments above, OP later showed updates for a developed kmod.
Short answer: yes, it's possible to set different IDs for a user process launched from a kmod.
After passing an init routine to call_usermodehelper_setup(), a related kernel service calls the init routine with a (struct cred *); various uid and gid members can be changed there. For more details, see call_usermodehelper_exec_async()
There are related suggestions for kmod versatility:
add a pair of #define for a set of default uid and gid
add support for module params to set other uid and gid
optionally provide cmd-line params when the module is loaded
For an example, see this link .
Based on #Milag's comment, the following code makes the new userspace process running in user-mode (using ps aux to inspect):
int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
return 0;
}
static int init_func(struct subprocess_info *info, struct cred *new)
{
printk("[%d]\n", current->pid);
alter_uid_gid(1000, 1000, new);
return 0;
}
static int user_process_fork(void *data)
{
struct subprocess_info *sub_info;
int ret = 0;
char *path = (char *)data;
char *argv[] = {path, NULL};
static char *envp[] = {"HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
init_func, NULL, NULL);
if (sub_info == NULL) return -ENOMEM;
ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
pr_info("%s: ret %d\n", __func__, ret);
do_exit(0);
return ret;
}

can we perform the operation using functions like fgets(), fputs(), feof(),etc. for the fifo file like we use for the normal file?

I have an assignment where I have to transfer the file from a client process to server process using fifo.I have tried to deal with fifo file as the other files we create in the system. It compiled without any error but it didn't execute properly.Can someone please give me an idea about the fifo file structure inside the computer system? What processes and functions are present for it ?Till now, I know how to use create(),read(),write(), open() function for fifo file.Also, I would be grateful if someone could help me to correct my program?
My client and server program are as follows:-
Client Program:-
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd;
char *myfifo ="/tmp/myfifo";
char str[80];
FILE *fp;
char filename[20];
printf("\nEnter filename: ");
gets(filename);
mkfifo(myfifo,0666);
fp = fopen(filename,"r");
if(fp == NULL)
{
printf("\nError opening the file");
exit(1);
}
fd = open(myfifo, O_WRONLY);
while(fgets(str,80,fp)!=NULL)
{
write(fd,str,strlen(str)+1);
}
close(fd);
fclose(fp);
return 0;
}
Client Program:-
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd1;
char *myfifo ="/tmp/myfifo";
char str1[80], filename[20];
FILE *fp1, *fp2;
fd1= open(myfifo, O_RDONLY);
fp1 = fopen(filename,"r");
fp2 = fopen(filename,"w");
while(!feof(fp1))
{
read(fd1,str1,strlen(str1)+1);
fputs(str1,fp2);
}
return 0;
}
Yes, but you have a few small problems in your programs. in the first:
write(fd, str, strlen(str)+1);
is a bit unconventional. This sends the string plus its end-of-string delimiter (\0) into the fd. One doesn't normally do this with strings, strlen(str) is probably what you want.
in the second:
fp1 = fopen(filename,"r");
fp2 = fopen(filename,"w");
filename has not been assigned a value, so both of these opens will almost certainly fail. When they do, they return a NULL pointer, so the first attempt to use them:
while(!feof(fp1))
will likely cause a segment violation. Also, you don't use fp1 anyways, so if feof(fp1) returned 1, it would always return 1. You want to base this loop on when the fifo is exhausted, which means there is no data in it, and nobody has it open for write. So changing this program around a bit yields:
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd1;
char *myfifo ="/tmp/myfifo";
char str1[80];
ssize_t n;
fd1= open(myfifo, O_RDONLY);
while ((n=read(fd1,str1,sizeof str1)) > 0)
{
fwrite(str1, 1, n, stdout);
}
return 0;
}
While this set of changes works, it doesn't address your other question, about using stdio functions with pipes. The answer is yes, and here is another functional rewrite of your second program:
#include<stdio.h>
int main()
{
char *myfifo ="/tmp/myfifo";
FILE *fp;
int c;
if ((fp = fopen(myfifo, "r")) != NULL) {
while ((c = getc(fp)) != EOF) {
putchar(c);
}
fclose(fp);
}
return 0;
}
Also, in the first, the critical bit with stdio:
...
FILE *fi = fopen(myfifo, "a");
while(fgets(str,80,fp)!=NULL)
{
fputs(str, fi);
}
fclose(fi);
...
as in the second, the loop could have been implemented with getc, putc.
A general refinement might be functions like these:
ssize_t FCopy(FILE *in, FILE *out) {
int c;
ssize_t len = 0;
while ((c = getc(in)) != EOF) {
len++;
if (putc(c, out) != c) {
return -len;
}
}
return len;
}
ssize_t FileAppend(char *from, char *to) {
FILE *in, *out;
ssize_t n = 0;
if ((in = fopen(from, "rb")) != NULL) {
if ((out = fopen(to, "ab")) != NULL) {
n = FCopy(in, out);
fclose(out);
} else {
n = -1;
}
fclose(in);
} else {
n = -1;
}
return n;
}
so your main would look more like:
...
char filename[80];
printf("Enter a file to store the data in: ");
if (fgets(filename, sizeof filename, stdin)) {
filename[strlen(filename)-1] = '\0';
if (FileAppend(myfifo, filename) < 0) {
printf("Error: could not save data to %s\n", filename);
}
}
....

FreeBSD kqueue filter only sometimes unblocks the waiting client

I am writing kqueue hooks for a character device that allows a client to block waiting for an EVFILT_READ. If I set my read filters code to always return one the kevent will return instantly. However, if the filter returns one at some later point in time nothing unblocks. For the following code the printf "After" never happens and in the filter code I can trivially get "filter_Read return 1" (immediately followed by a return 0)
Device (relevant excerpt)
static int
lowmem_filter_read(struct knote *kn, long hint)
{
mtx_assert(&lowmem_mtx, MA_OWNED);
if(manual_alert){
manual_alert=0;
printf("filter_Read return 1\n");
return 1;
}
printf("filter_Read return 0\n");
return 0;
}
static void
lowmem_filter_detach(struct knote *kn)
{
mtx_assert(&lowmem_mtx, MA_OWNED);
knlist_remove(&kl, kn, 0);
}
static struct filterops lowmem_filtops_read = {
.f_isfd = 1,
.f_detach = lowmem_filter_detach,
.f_event = lowmem_filter_read,
};
static int
lowmem_kqfilter(struct cdev *dev, struct knote *kn)
{
int err = EINVAL;
/* Figure out who needs service */
lowmem_lock();
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &lowmem_filtops_read;
knlist_add(&kl, kn, 1);
err = 0;
break;
default:
err = EOPNOTSUPP;
break;
}
lowmem_unlock();
return (err);
}
Client:
struct kevent ev;
struct timespec nullts = {0,0};
int fd=0;
int main(int argc, char **argv){
fd = open("/dev/lowmem", O_RDWR | O_NONBLOCK);
int kq=kqueue();
EV_SET(&ev,fd,EVFILT_READ, EV_ADD,0,0,NULL);
kevent(kq,&ev,1,NULL,0,&nullts);
for(;;){
printf("Starting\n");
int n=kevent(kq,NULL,0,&ev,1,NULL);
printf("After\n");
if(n>0){
printf("Something happened ev.fflags=%i\n",(int)ev.fflags);
}
}
return 0;
}

Reuse char * pointer needs to free and malloc again?

I would like to implement a main function such as in order to execute system commands. The following code is currently used :
int main(int argc, char *argv[])
{
size_t cmd_length;
char *cmd_buffer = NULL;
char *file = NULL;
char *ip = NULL;
int size;
if(argc == 3)
{
size = strlen(argv[1]);
file = (char*)malloc((size + 1)*sizeof(char));
strcpy(file, argv[1]);
size = strlen(argv[2]);
ip = (char*)malloc((size + 1)*sizeof(char));
strcpy(ip, argv[2]);
}
cmd_length = snprintf(NULL, 0, "tftp -g -r %s %s", file, ip);
cmd_buffer = malloc(cmd_length + 1);
if (cmd_buffer == NULL)
{
return -1;
}
snprintf(cmd_buffer, cmd_length + 1, "tftp -g -r %s %s", file, ip);
if(system(cmd_buffer) == 0)
{
then ...
}
{
return -1;
}
free(cmd_buffer);
cmd_buffer = NULL;
cmd_length = snprintf(NULL, 0, "tftp -g -r %s %s", DFT_FILE, DFT_IP);
cmd_buffer = malloc(cmd_length + 1);
if (cmd_buffer == NULL)
{
return -1;
}
snprintf(cmd_buffer, cmd_length + 1, "tftp -g -r %s %s", DFT_FILE, DFT_IP);
if(system(cmd_buffer) == 0)
{
then ...
}
{
return -1;
}
free(cmd_buffer);
free(file);
free(ip);
cmd_buffer = NULL;
file = NULL;
ip = NULL;
return 0;
}
Because I need to enter other commands, I am currently using the same cmd_buffer by using free() before reallocating memory. Is it the right way to do ? Some other commands might be required in the future.
Your program can be be greatly simplified if you use a common function to execute the system call. It doesn't even need to use malloc at all. Here's a partial implementation [Please pardon the gratuitous style cleanup]:
#include <stdarg.h>
int
execute_command(const char *fmt,...)
{
char cmd_buffer[5000];
int cmd_length;
va_list ap;
// NOTE: much simpler to used a fixed size buffer that is larger than
// needed
va_start(ap,fmt);
cmd_length = vsnprintf(cmd_buffer,sizeof(cmd_buffer),fmt,ap);
va_end(ap);
if (system(cmd_buffer) != 0)
return -1;
return 0;
}
int
main(int argc, char *argv[])
{
char *file = NULL;
char *ip = NULL;
// NOTE: I had to guess the intent if fewer arguments are passed (e.g. just
// skip as I did here, print message and abort?)
if (argc == 3) {
// NOTE: no need to malloc these, but if you did, see strdup(3)
file = argv[1];
ip = argv[2];
execute_command("tftp -g -r %s %s", file, ip);
}
execute_command("tftp -g -r %s %s", DFT_FILE, DFT_IP);
return 0;
}
Yes, you are essentially just re-using the pointer variable cmd_buffer which is fine. And for every malloc() there is a matching free(), which is good.
You should factor our common code into a function, for example runCommand(const char *command, ...) (using varargs).

Error when reading from Linux FIFO

In the embedded application I'm working on we have a serial port abstraction, and I'm currently working on a simulated variant of said abstraction to use when you are not running on the 'real' hardware. I'm using FIFO files for this, as you can then plug in whathever software you want to communicate with the actual application but I'm having trouble with the "read" function, which flags that you gave it an invalid fd. Though I have used debugging tools to verify that the fd passed to it is the same as has been opened earlier so it should be valid. I cannot find any cause for this problem.
FIFO files are opened through this function:
int createOpenFifo(const std::string& path, int flags)
{
int fd = open(path.c_str(), flags);
if (fd < 0)
{
mkfifo(path.c_str(), 0777);
fd = open(path.c_str(), flags);
if (fd < 0)
{
return -1;
}
}
return fd;
}
And the FIFOs are then written to using the following function:
int write_serial(handle_t handle, size_t length, const uint8_t* pData)
{
SerialFifoData* data = static_cast<SerialFifoData*>(handle);
size_t written = 0;
while (written < length)
{
int result = write(data->writeFd, pData + written, length - written);
if (result < 0)
{
return -1;
}
written += result;
}
return 0;
}
And finally read from using this function:
int read_serial(handle_t handle, size_t buffer_size, uint8_t* pBuffer, size_t* bytes_read)
{
SerialFifoData* data = static_cast<SerialFifoData*>(handle);
int return_val = read(data->readFd, pBuffer, buffer_size);
if (return_val < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK) // Non-blocking, no data
// which flag is raised
// varies between POSIX
// implementations
{
*bytes_read = 0;
return -2;
}
return -1;
}
*bytes_read = return_val;
return 0;
}
I have verified that each function recieves correct input, and the read and write calls are nearly identical to those used for the actual serial port code (the only difference is how the FD is extracted from the handle) where they work just fine.

Resources