1 /* This file contains file and directory reading file system call handlers.
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
8 * April 2009 (D.C. van Moolenbroek)
15 #define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
17 /*===========================================================================*
19 *===========================================================================*/
22 /* Read data from a file.
31 if ((ino
= find_inode(m_in
.REQ_INODE_NR
)) == NULL
)
34 if (IS_DIR(ino
)) return EISDIR
;
36 if ((r
= get_handle(ino
)) != OK
)
39 pos
= make64(m_in
.REQ_SEEK_POS_LO
, m_in
.REQ_SEEK_POS_HI
);
40 count
= m_in
.REQ_NBYTES
;
44 /* Use the buffer from below to eliminate extra copying. */
45 size
= sffs_table
->t_readbuf(&ptr
);
49 chunk
= MIN(count
, size
);
51 if ((r
= sffs_table
->t_read(ino
->i_file
, ptr
, chunk
, pos
)) <= 0)
56 r
= sys_safecopyto(m_in
.m_source
, m_in
.REQ_GRANT
, off
,
57 (vir_bytes
) ptr
, chunk
);
64 pos
= add64u(pos
, chunk
);
70 m_out
.RES_SEEK_POS_HI
= ex64hi(pos
);
71 m_out
.RES_SEEK_POS_LO
= ex64lo(pos
);
72 m_out
.RES_NBYTES
= off
;
77 /*===========================================================================*
79 *===========================================================================*/
82 /* Retrieve directory entries.
84 char name
[NAME_MAX
+1];
85 struct inode
*ino
, *child
;
87 struct sffs_attr attr
;
88 size_t len
, off
, user_off
, user_left
;
91 /* must be at least sizeof(struct dirent) + NAME_MAX */
92 static char buf
[BLOCK_SIZE
];
94 attr
.a_mask
= SFFS_ATTR_MODE
;
96 if ((ino
= find_inode(m_in
.REQ_INODE_NR
)) == NULL
)
99 if (m_in
.REQ_SEEK_POS_HI
!= 0) return EINVAL
;
101 if (!IS_DIR(ino
)) return ENOTDIR
;
103 /* We are going to need at least one free inode to store children in. */
104 if (!have_free_inode()) return ENFILE
;
106 /* If we don't have a directory handle yet, get one now. */
107 if ((r
= get_handle(ino
)) != OK
)
112 user_left
= m_in
.REQ_MEM_SIZE
;
114 /* We use the seek position as file index number. The first position is for
115 * the "." entry, the second position is for the ".." entry, and the next
116 * position numbers each represent a file in the directory.
118 for (pos
= m_in
.REQ_SEEK_POS_LO
; ; pos
++) {
119 /* Determine which inode and name to use for this entry.
120 * We have no idea whether the host will give us "." and/or "..",
121 * so generate our own and skip those from the host.
132 /* Entry for "..", but only when there is a parent. */
133 if (ino
->i_parent
== NULL
)
136 child
= ino
->i_parent
;
143 /* Any other entry, not being "." or "..". */
144 r
= sffs_table
->t_readdir(ino
->i_dir
, pos
- 2, name
,
145 sizeof(name
), &attr
);
148 /* No more entries? Then close the handle and stop. */
155 /* FIXME: what if the error is ENAMETOOLONG? */
159 if (!strcmp(name
, ".") || !strcmp(name
, ".."))
162 if ((child
= lookup_dentry(ino
, name
)) == NULL
) {
163 child
= get_free_inode();
165 /* We were promised a free inode! */
166 assert(child
!= NULL
);
168 child
->i_flags
= MODE_TO_DIRFLAG(attr
.a_mode
);
170 add_dentry(ino
, name
, child
);
174 len
= DWORD_ALIGN(sizeof(struct dirent
) + strlen(name
));
176 /* Is the user buffer too small to store another record?
177 * Note that we will be rerequesting the same dentry upon a subsequent
178 * getdents call this way, but we really need the name length for this.
180 if (user_off
+ off
+ len
> user_left
) {
183 /* Is the user buffer too small for even a single record? */
184 if (user_off
== 0 && off
== 0)
190 /* If our own buffer cannot contain the new record, copy out first. */
191 if (off
+ len
> sizeof(buf
)) {
192 r
= sys_safecopyto(m_in
.m_source
, m_in
.REQ_GRANT
,
193 user_off
, (vir_bytes
) buf
, off
);
206 /* Fill in the actual directory entry. */
207 dent
= (struct dirent
*) &buf
[off
];
208 dent
->d_ino
= INODE_NR(child
);
210 dent
->d_reclen
= len
;
211 strcpy(dent
->d_name
, name
);
218 /* If there is anything left in our own buffer, copy that out now. */
220 r
= sys_safecopyto(m_in
.m_source
, m_in
.REQ_GRANT
, user_off
,
221 (vir_bytes
) buf
, off
);
229 m_out
.RES_SEEK_POS_HI
= 0;
230 m_out
.RES_SEEK_POS_LO
= pos
;
231 m_out
.RES_NBYTES
= user_off
;