/*===========================================================================* * fs_read * *===========================================================================*/ int fs_read(void) { /* Read from a file. */ cp_grant_id_t gid; struct inode *node; off_t pos; size_t len; char *ptr; int r; if (fs_m_in.REQ_SEEK_POS_HI != 0) return EIO; /* Try to get inode by to its inode number. */ if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL) return EINVAL; /* Check whether the node is a regular file. */ if (!S_ISREG(node->i_stat.mode)) return EINVAL; /* Get the values from the request message. */ gid = fs_m_in.REQ_GRANT; pos = fs_m_in.REQ_SEEK_POS_LO; /* Call the read hook, if any. */ if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) { len = fs_m_in.REQ_NBYTES; /* On success, the read hook provides us with a pointer to the * resulting data. This avoids copying overhead. */ r = vtreefs_hooks->read_hook(node, pos, &ptr, &len, get_inode_cbdata(node)); assert(len >= 0 && len <= fs_m_in.REQ_NBYTES); /* Copy the resulting data to user space. */ if (r == OK && len > 0) { r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0, (vir_bytes) ptr, len, D); } } else { /* Feign an empty file. */ r = OK; len = 0; } if (r == OK) { fs_m_out.RES_SEEK_POS_HI = 0; fs_m_out.RES_SEEK_POS_LO = pos + len; fs_m_out.RES_NBYTES = len; } return r; }
/* * Resolve a path string to an inode. */ int fs_lookup(ino_t dir_nr, char * name, struct fsdriver_node * node_details, int * is_mountpt) { struct inode *node, *child; int r; if ((node = find_inode(dir_nr)) == NULL) return EINVAL; if (!S_ISDIR(node->i_stat.mode)) return ENOTDIR; if (strlen(name) > PNAME_MAX) return ENAMETOOLONG; if (!strcmp(name, ".")) { /* Stay in the given directory. */ child = node; } else if (!strcmp(name, "..")) { /* Progress into the parent directory. */ if ((child = get_parent_inode(node)) == NULL) return ENOENT; /* deleted? should not be possible */ } else { /* Progress into a directory entry. Call the lookup hook, if * present, before doing the actual lookup. */ if (!is_inode_deleted(node) && vtreefs_hooks->lookup_hook != NULL) { r = vtreefs_hooks->lookup_hook(node, name, get_inode_cbdata(node)); if (r != OK) return r; } if ((child = get_inode_by_name(node, name)) == NULL) return ENOENT; } /* On success, open the resulting file and return its details. */ ref_inode(child); node_details->fn_ino_nr = get_inode_number(child); node_details->fn_mode = child->i_stat.mode; node_details->fn_size = child->i_stat.size; node_details->fn_uid = child->i_stat.uid; node_details->fn_gid = child->i_stat.gid; node_details->fn_dev = child->i_stat.dev; *is_mountpt = FALSE; return OK; }
/*===========================================================================* * fs_stat * *===========================================================================*/ PUBLIC int fs_stat(void) { /* Retrieve file status. */ char path[PATH_MAX]; struct stat statbuf; time_t cur_time; struct inode *node; int r; if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL) return EINVAL; memset(&statbuf, 0, sizeof(struct stat)); /* Fill in the basic info. */ statbuf.st_dev = fs_dev; statbuf.st_ino = get_inode_number(node); statbuf.st_mode = node->i_stat.mode; statbuf.st_nlink = !is_inode_deleted(node); statbuf.st_uid = node->i_stat.uid; statbuf.st_gid = node->i_stat.gid; statbuf.st_rdev = (dev_t) node->i_stat.dev; statbuf.st_size = node->i_stat.size; /* If it is a symbolic link, return the size of the link target. */ if (S_ISLNK(node->i_stat.mode) && vtreefs_hooks->rdlink_hook != NULL) { r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path), get_inode_cbdata(node)); if (r == OK) statbuf.st_size = strlen(path); } /* Take the current time as file time for all files. */ cur_time = time(NULL); statbuf.st_atime = cur_time; statbuf.st_mtime = cur_time; statbuf.st_ctime = cur_time; /* Copy the struct to user space. */ return sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, 0, (vir_bytes) &statbuf, (phys_bytes) sizeof(statbuf), D); }
/*===========================================================================* * fs_read * *===========================================================================*/ ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t pos, int __unused call) { /* Read from a file. */ struct inode *node; size_t len; char *ptr; int r; /* Try to get inode by its inode number. */ if ((node = find_inode(ino_nr)) == NULL) return EINVAL; /* Check whether the node is a regular file. */ if (!S_ISREG(node->i_stat.mode)) return EINVAL; /* Call the read hook, if any. */ if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) { len = bytes; /* On success, the read hook provides us with a pointer to the * resulting data. This avoids copying overhead. */ r = vtreefs_hooks->read_hook(node, pos, &ptr, &len, get_inode_cbdata(node)); assert(len <= bytes); /* Copy the resulting data to user space. */ if (r == OK && len > 0) r = fsdriver_copyout(data, 0, ptr, len); } else { /* Feign an empty file. */ r = OK; len = 0; } return (r != OK) ? r : len; }
/*===========================================================================* * fs_stat * *===========================================================================*/ int fs_stat(ino_t ino_nr, struct stat *buf) { /* Retrieve file status. */ char path[PATH_MAX]; time_t cur_time; struct inode *node; int r; if ((node = find_inode(ino_nr)) == NULL) return EINVAL; /* Fill in the basic info. */ buf->st_dev = fs_dev; buf->st_ino = get_inode_number(node); buf->st_mode = node->i_stat.mode; buf->st_nlink = !is_inode_deleted(node); buf->st_uid = node->i_stat.uid; buf->st_gid = node->i_stat.gid; buf->st_rdev = (dev_t) node->i_stat.dev; buf->st_size = node->i_stat.size; /* If it is a symbolic link, return the size of the link target. */ if (S_ISLNK(node->i_stat.mode) && vtreefs_hooks->rdlink_hook != NULL) { r = vtreefs_hooks->rdlink_hook(node, path, sizeof(path), get_inode_cbdata(node)); if (r == OK) buf->st_size = strlen(path); } /* Take the current time as file time for all files. */ cur_time = clock_time(NULL); buf->st_atime = cur_time; buf->st_mtime = cur_time; buf->st_ctime = cur_time; return OK; }
/*===========================================================================* * fs_getdents * *===========================================================================*/ ssize_t fs_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t *posp) { /* Retrieve directory entries. */ struct fsdriver_dentry fsdentry; struct inode *node, *child; const char *name; off_t pos; int r, skip, get_next, indexed; static char buf[GETDENTS_BUFSIZ]; if (*posp >= ULONG_MAX) return EIO; if ((node = find_inode(ino_nr)) == NULL) return EINVAL; indexed = node->i_indexed; get_next = FALSE; child = NULL; /* Call the getdents hook, if any, to "refresh" the directory. */ if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) { r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node)); if (r != OK) return r; } fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf)); do { /* Determine which inode and name to use for this entry. */ pos = (*posp)++; if (pos == 0) { /* The "." entry. */ child = node; name = "."; } else if (pos == 1) { /* The ".." entry. */ child = get_parent_inode(node); if (child == NULL) child = node; name = ".."; } else if (pos - 2 < indexed) { /* All indexed entries. */ child = get_inode_by_index(node, pos - 2); /* If there is no inode with this particular index, * continue with the next index number. */ if (child == NULL) continue; name = child->i_name; } else { /* All non-indexed entries. */ /* If this is the first loop iteration, first get to * the non-indexed child identified by the current * position. */ if (get_next == FALSE) { skip = pos - indexed - 2; child = get_first_inode(node); /* Skip indexed children. */ while (child != NULL && child->i_index != NO_INDEX) child = get_next_inode(child); /* Skip to the right position. */ while (child != NULL && skip-- > 0) child = get_next_inode(child); get_next = TRUE; } else { child = get_next_inode(child); } /* No more children? Then stop. */ if (child == NULL) break; assert(!is_inode_deleted(child)); name = child->i_name; } /* Add the directory entry to the output. */ r = fsdriver_dentry_add(&fsdentry, (ino_t) get_inode_number(child), name, strlen(name), IFTODT(child->i_stat.mode)); if (r < 0) return r; } while (r > 0); return fsdriver_dentry_finish(&fsdentry); }
/*===========================================================================* * fs_getdents * *===========================================================================*/ int fs_getdents(void) { /* Retrieve directory entries. */ struct inode *node, *child = NULL; struct dirent *dent; char *name; size_t len, off, user_off, user_left; off_t pos; int r, skip, get_next, indexed; static char buf[GETDENTS_BUFSIZ]; if (fs_m_in.REQ_SEEK_POS_HI != 0) return EIO; if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL) return EINVAL; off = 0; user_off = 0; user_left = fs_m_in.REQ_MEM_SIZE; indexed = node->i_indexed; get_next = FALSE; child = NULL; /* Call the getdents hook, if any, to "refresh" the directory. */ if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) { r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node)); if (r != OK) return r; } for (pos = fs_m_in.REQ_SEEK_POS_LO; ; pos++) { /* Determine which inode and name to use for this entry. */ if (pos == 0) { /* The "." entry. */ child = node; name = "."; } else if (pos == 1) { /* The ".." entry. */ child = get_parent_inode(node); if (child == NULL) child = node; name = ".."; } else if (pos - 2 < indexed) { /* All indexed entries. */ child = get_inode_by_index(node, pos - 2); /* If there is no inode with this particular index, * continue with the next index number. */ if (child == NULL) continue; name = child->i_name; } else { /* All non-indexed entries. */ /* If this is the first loop iteration, first get to * the non-indexed child identified by the current * position. */ if (get_next == FALSE) { skip = pos - indexed - 2; child = get_first_inode(node); /* Skip indexed children. */ while (child != NULL && child->i_index != NO_INDEX) child = get_next_inode(child); /* Skip to the right position. */ while (child != NULL && skip-- > 0) child = get_next_inode(child); get_next = TRUE; } else { child = get_next_inode(child); } /* No more children? Then stop. */ if (child == NULL) break; assert(!is_inode_deleted(child)); name = child->i_name; } len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name)); /* Is the user buffer too small to store another record? */ if (user_off + off + len > user_left) { /* Is the user buffer too small for even a single * record? */ if (user_off == 0 && off == 0) return EINVAL; break; } /* If our own buffer cannot contain the new record, copy out * first. */ if (off + len > sizeof(buf)) { r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, user_off, (vir_bytes) buf, off, D); if (r != OK) return r; user_off += off; user_left -= off; off = 0; } /* Fill in the actual directory entry. */ dent = (struct dirent *) &buf[off]; dent->d_ino = get_inode_number(child); dent->d_off = pos; dent->d_reclen = len; strcpy(dent->d_name, name); off += len; } /* If there is anything left in our own buffer, copy that out now. */ if (off > 0) { r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT, user_off, (vir_bytes) buf, off, D); if (r != OK) return r; user_off += off; } fs_m_out.RES_SEEK_POS_HI = 0; fs_m_out.RES_SEEK_POS_LO = pos; fs_m_out.RES_NBYTES = user_off; return OK; }