/*===========================================================================* * go_down * *===========================================================================*/ static int go_down( char path[PATH_MAX], /* path to add the name to */ struct inode *parent, /* inode of the current directory */ char *name, /* name of the directory entry */ struct inode **res_ino, /* place to store resulting inode */ struct sffs_attr *attr /* place to store inode attributes */ ) { /* Given a directory inode and a name, progress into a directory entry. */ struct inode *ino; int r, stale = 0; if ((r = push_path(path, name)) != OK) return r; dprintf(("%s: go_down: name '%s', path now '%s'\n", sffs_name, name, path)); ino = lookup_dentry(parent, name); dprintf(("%s: lookup_dentry('%s') returned %p\n", sffs_name, name, ino)); if (ino != NULL) r = verify_path(path, ino, attr, &stale); else r = sffs_table->t_getattr(path, attr); dprintf(("%s: path query returned %d\n", sffs_name, r)); if (r != OK) { if (ino != NULL) { put_inode(ino); ino = NULL; } if (!stale) return r; } dprintf(("%s: name '%s'\n", sffs_name, name)); if (ino == NULL) { if ((ino = get_free_inode()) == NULL) return ENFILE; dprintf(("%s: inode %p ref %d\n", sffs_name, ino, ino->i_ref)); ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode); add_dentry(parent, name, ino); } *res_ino = ino; return OK; }
/*===========================================================================* * verify_path * *===========================================================================*/ int verify_path(char path[PATH_MAX], struct inode *ino, struct sffs_attr *attr, int *stale) { /* Given a path, and the inode associated with that path, verify if the inode * still matches the real world. Obtain the attributes of the file identified * by the given path, and see if they match. If not, possibly mark the inode * as deleted and return an error. Only upon success is the inode guaranteed * to be usable. * * The caller must set the a_mask field of the passed attr struct. * If 'stale' is not NULL, the value it points to must be initialized to 0, * and will be set to 1 if the path was valid but the inode wasn't. */ int r; attr->a_mask |= SFFS_ATTR_MODE; r = sffs_table->t_getattr(path, attr); dprintf(("%s: verify_path: getattr('%s') returned %d\n", sffs_name, path, r)); if (r != OK) { /* If we are told that the path does not exist, delete the inode */ if (r == ENOENT || r == ENOTDIR) del_dentry(ino); return r; /* path isn't valid */ } /* If the file type (reg, dir) isn't what we thought, delete the inode */ if ((ino->i_flags & I_DIR) != MODE_TO_DIRFLAG(attr->a_mode)) { del_dentry(ino); if (stale != NULL) *stale = 1; return ENOENT; /* path is valid, inode wasn't */ } return OK; /* path and inode are valid */ }
/*===========================================================================* * do_getdents * *===========================================================================*/ int do_getdents() { /* Retrieve directory entries. */ char name[NAME_MAX+1]; struct inode *ino, *child; struct dirent *dent; struct sffs_attr attr; size_t len, off, user_off, user_left; off_t pos; int r; /* must be at least sizeof(struct dirent) + NAME_MAX */ static char buf[BLOCK_SIZE]; attr.a_mask = SFFS_ATTR_MODE; if ((ino = find_inode(m_in.REQ_INODE_NR)) == NULL) return EINVAL; if (m_in.REQ_SEEK_POS_HI != 0) return EINVAL; if (!IS_DIR(ino)) return ENOTDIR; /* We are going to need at least one free inode to store children in. */ if (!have_free_inode()) return ENFILE; /* If we don't have a directory handle yet, get one now. */ if ((r = get_handle(ino)) != OK) return r; off = 0; user_off = 0; user_left = m_in.REQ_MEM_SIZE; /* We use the seek position as file index number. The first position is for * the "." entry, the second position is for the ".." entry, and the next * position numbers each represent a file in the directory. */ for (pos = m_in.REQ_SEEK_POS_LO; ; pos++) { /* Determine which inode and name to use for this entry. * We have no idea whether the host will give us "." and/or "..", * so generate our own and skip those from the host. */ if (pos == 0) { /* Entry for ".". */ child = ino; strcpy(name, "."); get_inode(child); } else if (pos == 1) { /* Entry for "..", but only when there is a parent. */ if (ino->i_parent == NULL) continue; child = ino->i_parent; strcpy(name, ".."); get_inode(child); } else { /* Any other entry, not being "." or "..". */ r = sffs_table->t_readdir(ino->i_dir, pos - 2, name, sizeof(name), &attr); if (r != OK) { /* No more entries? Then close the handle and stop. */ if (r == ENOENT) { put_handle(ino); break; } /* FIXME: what if the error is ENAMETOOLONG? */ return r; } if (!strcmp(name, ".") || !strcmp(name, "..")) continue; if ((child = lookup_dentry(ino, name)) == NULL) { child = get_free_inode(); /* We were promised a free inode! */ assert(child != NULL); child->i_flags = MODE_TO_DIRFLAG(attr.a_mode); add_dentry(ino, name, child); } } len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name)); /* Is the user buffer too small to store another record? * Note that we will be rerequesting the same dentry upon a subsequent * getdents call this way, but we really need the name length for this. */ if (user_off + off + len > user_left) { put_inode(child); /* 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(m_in.m_source, m_in.REQ_GRANT, user_off, (vir_bytes) buf, off, D); if (r != OK) { put_inode(child); return r; } user_off += off; user_left -= off; off = 0; } /* Fill in the actual directory entry. */ dent = (struct dirent *) &buf[off]; dent->d_ino = INODE_NR(child); dent->d_off = pos; dent->d_reclen = len; strcpy(dent->d_name, name); off += len; put_inode(child); } /* If there is anything left in our own buffer, copy that out now. */ if (off > 0) { r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, user_off, (vir_bytes) buf, off, D); if (r != OK) return r; user_off += off; } m_out.RES_SEEK_POS_HI = 0; m_out.RES_SEEK_POS_LO = pos; m_out.RES_NBYTES = user_off; return OK; }
/*===========================================================================* * do_getdents * *===========================================================================*/ ssize_t do_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t *posp) { /* Retrieve directory entries. */ struct fsdriver_dentry fsdentry; char name[NAME_MAX+1]; struct inode *ino, *child; struct sffs_attr attr; off_t pos; int r; /* must be at least sizeof(struct dirent) + NAME_MAX */ static char buf[BLOCK_SIZE]; if ((ino = find_inode(ino_nr)) == NULL) return EINVAL; if (!IS_DIR(ino)) return ENOTDIR; if (*posp < 0 || *posp >= ULONG_MAX) return EINVAL; /* We are going to need at least one free inode to store children in. */ if (!have_free_inode()) return ENFILE; /* If we don't have a directory handle yet, get one now. */ if ((r = get_handle(ino)) != OK) return r; fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf)); /* We use the seek position as file index number. The first position is for * the "." entry, the second position is for the ".." entry, and the next * position numbers each represent a file in the directory. */ do { /* Determine which inode and name to use for this entry. * We have no idea whether the host will give us "." and/or "..", * so generate our own and skip those from the host. */ pos = (*posp)++; if (pos == 0) { /* Entry for ".". */ child = ino; strcpy(name, "."); get_inode(child); } else if (pos == 1) { /* Entry for "..", but only when there is a parent. */ if (ino->i_parent == NULL) continue; child = ino->i_parent; strcpy(name, ".."); get_inode(child); } else { /* Any other entry, not being "." or "..". */ attr.a_mask = SFFS_ATTR_MODE; r = sffs_table->t_readdir(ino->i_dir, pos - 2, name, sizeof(name), &attr); if (r != OK) { /* No more entries? Then close the handle and stop. */ if (r == ENOENT) { put_handle(ino); break; } /* FIXME: what if the error is ENAMETOOLONG? */ return r; } if (!strcmp(name, ".") || !strcmp(name, "..")) continue; if ((child = lookup_dentry(ino, name)) == NULL) { child = get_free_inode(); /* We were promised a free inode! */ assert(child != NULL); child->i_flags = MODE_TO_DIRFLAG(attr.a_mode); add_dentry(ino, name, child); } } r = fsdriver_dentry_add(&fsdentry, INODE_NR(child), name, strlen(name), IS_DIR(child) ? DT_DIR : DT_REG); put_inode(child); if (r < 0) return r; } while (r > 0); return fsdriver_dentry_finish(&fsdentry); }