1 /* This file contains file and directory reading file system call handlers. 2 * 3 * The entry points into this file are: 4 * do_read perform the READ file system call 5 * do_getdents perform the GETDENTS file system call 6 * 7 * Created: 8 * April 2009 (D.C. van Moolenbroek) 9 */ 10 11 #include "inc.h" 12 13 #include <dirent.h> 14 15 #define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1)) 16 17 /*===========================================================================* 18 * do_read * 19 *===========================================================================*/ 20 int do_read(void) 21 { 22 /* Read data from a file. 23 */ 24 struct inode *ino; 25 off_t pos; 26 size_t count, size; 27 vir_bytes off; 28 char *ptr; 29 int r, chunk; 30 31 if ((ino = find_inode(m_in.m_vfs_fs_readwrite.inode)) == NULL) 32 return EINVAL; 33 34 if (IS_DIR(ino)) return EISDIR; 35 36 if ((r = get_handle(ino)) != OK) 37 return r; 38 39 pos = m_in.m_vfs_fs_readwrite.seek_pos; 40 count = m_in.m_vfs_fs_readwrite.nbytes; 41 42 assert(count > 0); 43 44 /* Use the buffer from below to eliminate extra copying. */ 45 size = sffs_table->t_readbuf(&ptr); 46 off = 0; 47 48 while (count > 0) { 49 chunk = MIN(count, size); 50 51 if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0) 52 break; 53 54 chunk = r; 55 56 r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_readwrite.grant, off, 57 (vir_bytes) ptr, chunk); 58 59 if (r != OK) 60 break; 61 62 count -= chunk; 63 off += chunk; 64 pos += chunk; 65 } 66 67 if (r < 0) 68 return r; 69 70 m_out.m_fs_vfs_readwrite.seek_pos = pos; 71 m_out.m_fs_vfs_readwrite.nbytes = off; 72 73 return OK; 74 } 75 76 /*===========================================================================* 77 * do_getdents * 78 *===========================================================================*/ 79 int do_getdents(void) 80 { 81 /* Retrieve directory entries. 82 */ 83 char name[NAME_MAX+1]; 84 struct inode *ino, *child; 85 struct dirent *dent; 86 struct sffs_attr attr; 87 size_t len, off, user_off, user_left; 88 off_t pos; 89 int r, namelen; 90 /* must be at least sizeof(struct dirent) + NAME_MAX */ 91 static char buf[BLOCK_SIZE]; 92 93 attr.a_mask = SFFS_ATTR_MODE; 94 95 if ((ino = find_inode(m_in.m_vfs_fs_getdents.inode)) == NULL) 96 return EINVAL; 97 98 if(m_in.m_vfs_fs_getdents.seek_pos >= ULONG_MAX) return EINVAL; 99 100 if (!IS_DIR(ino)) return ENOTDIR; 101 102 /* We are going to need at least one free inode to store children in. */ 103 if (!have_free_inode()) return ENFILE; 104 105 /* If we don't have a directory handle yet, get one now. */ 106 if ((r = get_handle(ino)) != OK) 107 return r; 108 109 off = 0; 110 user_off = 0; 111 user_left = m_in.m_vfs_fs_getdents.mem_size; 112 113 /* We use the seek position as file index number. The first position is for 114 * the "." entry, the second position is for the ".." entry, and the next 115 * position numbers each represent a file in the directory. 116 */ 117 for (pos = m_in.m_vfs_fs_getdents.seek_pos; ; pos++) { 118 /* Determine which inode and name to use for this entry. 119 * We have no idea whether the host will give us "." and/or "..", 120 * so generate our own and skip those from the host. 121 */ 122 if (pos == 0) { 123 /* Entry for ".". */ 124 child = ino; 125 126 strcpy(name, "."); 127 128 get_inode(child); 129 } 130 else if (pos == 1) { 131 /* Entry for "..", but only when there is a parent. */ 132 if (ino->i_parent == NULL) 133 continue; 134 135 child = ino->i_parent; 136 137 strcpy(name, ".."); 138 139 get_inode(child); 140 } 141 else { 142 /* Any other entry, not being "." or "..". */ 143 r = sffs_table->t_readdir(ino->i_dir, pos - 2, name, 144 sizeof(name), &attr); 145 146 if (r != OK) { 147 /* No more entries? Then close the handle and stop. */ 148 if (r == ENOENT) { 149 put_handle(ino); 150 151 break; 152 } 153 154 /* FIXME: what if the error is ENAMETOOLONG? */ 155 return r; 156 } 157 158 if (!strcmp(name, ".") || !strcmp(name, "..")) 159 continue; 160 161 if ((child = lookup_dentry(ino, name)) == NULL) { 162 child = get_free_inode(); 163 164 /* We were promised a free inode! */ 165 assert(child != NULL); 166 167 child->i_flags = MODE_TO_DIRFLAG(attr.a_mode); 168 169 add_dentry(ino, name, child); 170 } 171 } 172 173 /* record length incl. alignment. */ 174 namelen = strlen(name); 175 len = _DIRENT_RECLEN(dent, namelen); 176 177 /* Is the user buffer too small to store another record? 178 * Note that we will be rerequesting the same dentry upon a subsequent 179 * getdents call this way, but we really need the name length for this. 180 */ 181 if (user_off + off + len > user_left) { 182 put_inode(child); 183 184 /* Is the user buffer too small for even a single record? */ 185 if (user_off == 0 && off == 0) 186 return EINVAL; 187 188 break; 189 } 190 191 /* If our own buffer cannot contain the new record, copy out first. */ 192 if (off + len > sizeof(buf)) { 193 r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_getdents.grant, 194 user_off, (vir_bytes) buf, off); 195 196 if (r != OK) { 197 put_inode(child); 198 199 return r; 200 } 201 202 user_off += off; 203 user_left -= off; 204 off = 0; 205 } 206 207 /* Fill in the actual directory entry. */ 208 dent = (struct dirent *) &buf[off]; 209 dent->d_ino = INODE_NR(child); 210 dent->d_reclen = len; 211 dent->d_namlen = namelen; 212 dent->d_type = IS_DIR(child) ? DT_DIR : DT_REG; 213 strcpy(dent->d_name, name); 214 215 off += len; 216 217 put_inode(child); 218 } 219 220 /* If there is anything left in our own buffer, copy that out now. */ 221 if (off > 0) { 222 r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_getdents.grant, user_off, 223 (vir_bytes) buf, off); 224 225 if (r != OK) 226 return r; 227 228 user_off += off; 229 } 230 231 m_out.m_fs_vfs_getdents.seek_pos = pos; 232 m_out.m_fs_vfs_getdents.nbytes = user_off; 233 234 return OK; 235 } 236