Fix getdents cannot output all entries in a directory
This commit is contained in:
		
							parent
							
								
									c4c3315c06
								
							
						
					
					
						commit
						d7b994bc7d
					
				| @ -8,7 +8,7 @@ pub fn do_getdents(fd: FileDesc, buf: &mut [u8]) -> Result<usize> { | |||||||
|     getdents_common::<()>(fd, buf) |     getdents_common::<()>(fd, buf) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn getdents_common<T: DirentType + Copy + Default>(fd: FileDesc, buf: &mut [u8]) -> Result<usize> { | fn getdents_common<T: DirentType + Copy>(fd: FileDesc, buf: &mut [u8]) -> Result<usize> { | ||||||
|     debug!( |     debug!( | ||||||
|         "getdents: fd: {}, buf: {:?}, buf_size: {}", |         "getdents: fd: {}, buf: {:?}, buf_size: {}", | ||||||
|         fd, |         fd, | ||||||
| @ -33,8 +33,8 @@ fn getdents_common<T: DirentType + Copy + Default>(fd: FileDesc, buf: &mut [u8]) | |||||||
|             } |             } | ||||||
|             Ok(name) => name, |             Ok(name) => name, | ||||||
|         }; |         }; | ||||||
|         // TODO: get ino from dirent
 |         // TODO: get ino and type from dirent
 | ||||||
|         let dirent = LinuxDirent::<T>::new(1, &name); |         let dirent = LinuxDirent::<T>::new(1, &name, DT_UNKNOWN); | ||||||
|         if let Err(e) = writer.try_write(&dirent, &name) { |         if let Err(e) = writer.try_write(&dirent, &name) { | ||||||
|             file_ref.seek(SeekFrom::Current(-1))?; |             file_ref.seek(SeekFrom::Current(-1))?; | ||||||
|             if writer.written_size == 0 { |             if writer.written_size == 0 { | ||||||
| @ -47,8 +47,10 @@ fn getdents_common<T: DirentType + Copy + Default>(fd: FileDesc, buf: &mut [u8]) | |||||||
|     Ok(writer.written_size) |     Ok(writer.written_size) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const DT_UNKNOWN: u8 = 0; | ||||||
|  | 
 | ||||||
| #[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes.
 | #[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes.
 | ||||||
| struct LinuxDirent<T: DirentType + Copy + Default> { | struct LinuxDirent<T: DirentType + Copy> { | ||||||
|     /// Inode number
 |     /// Inode number
 | ||||||
|     ino: u64, |     ino: u64, | ||||||
|     /// Offset to next structure
 |     /// Offset to next structure
 | ||||||
| @ -61,15 +63,20 @@ struct LinuxDirent<T: DirentType + Copy + Default> { | |||||||
|     name: [u8; 0], |     name: [u8; 0], | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: DirentType + Copy + Default> LinuxDirent<T> { | impl<T: DirentType + Copy> LinuxDirent<T> { | ||||||
|     fn new(ino: u64, name: &str) -> Self { |     fn new(ino: u64, name: &str, d_type: u8) -> Self { | ||||||
|         let ori_len = ::core::mem::size_of::<LinuxDirent<T>>() + name.len() + 1; |         let ori_len = if !T::at_the_end_of_linux_dirent() { | ||||||
|  |             core::mem::size_of::<LinuxDirent<T>>() + name.len() + 1 | ||||||
|  |         } else { | ||||||
|  |             // pad the file type at the end
 | ||||||
|  |             core::mem::size_of::<LinuxDirent<T>>() + name.len() + 1 + core::mem::size_of::<u8>() | ||||||
|  |         }; | ||||||
|         let len = align_up(ori_len, 8); // align up to 8 bytes
 |         let len = align_up(ori_len, 8); // align up to 8 bytes
 | ||||||
|         Self { |         Self { | ||||||
|             ino, |             ino, | ||||||
|             offset: 0, |             offset: 0, | ||||||
|             reclen: len as u16, |             reclen: len as u16, | ||||||
|             type_: Default::default(), |             type_: T::set_type(d_type), | ||||||
|             name: [], |             name: [], | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -79,9 +86,9 @@ impl<T: DirentType + Copy + Default> LinuxDirent<T> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: DirentType + Copy + Default> Copy for LinuxDirent<T> {} | impl<T: DirentType + Copy> Copy for LinuxDirent<T> {} | ||||||
| 
 | 
 | ||||||
| impl<T: DirentType + Copy + Default> Clone for LinuxDirent<T> { | impl<T: DirentType + Copy> Clone for LinuxDirent<T> { | ||||||
|     fn clone(&self) -> Self { |     fn clone(&self) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             ino: self.ino, |             ino: self.ino, | ||||||
| @ -93,10 +100,27 @@ impl<T: DirentType + Copy + Default> Clone for LinuxDirent<T> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| trait DirentType {} | trait DirentType { | ||||||
|  |     fn set_type(d_type: u8) -> Self; | ||||||
|  |     fn at_the_end_of_linux_dirent() -> bool; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| impl DirentType for u8 {} | impl DirentType for u8 { | ||||||
| impl DirentType for () {} |     fn set_type(d_type: u8) -> Self { | ||||||
|  |         d_type | ||||||
|  |     } | ||||||
|  |     fn at_the_end_of_linux_dirent() -> bool { | ||||||
|  |         false | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl DirentType for () { | ||||||
|  |     fn set_type(d_type: u8) -> Self { | ||||||
|  |         Default::default() | ||||||
|  |     } | ||||||
|  |     fn at_the_end_of_linux_dirent() -> bool { | ||||||
|  |         true | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| struct DirentBufWriter<'a> { | struct DirentBufWriter<'a> { | ||||||
|     buf: &'a mut [u8], |     buf: &'a mut [u8], | ||||||
| @ -114,7 +138,7 @@ impl<'a> DirentBufWriter<'a> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn try_write<T: DirentType + Copy + Default>( |     fn try_write<T: DirentType + Copy>( | ||||||
|         &mut self, |         &mut self, | ||||||
|         dirent: &LinuxDirent<T>, |         dirent: &LinuxDirent<T>, | ||||||
|         name: &str, |         name: &str, | ||||||
| @ -127,6 +151,21 @@ impl<'a> DirentBufWriter<'a> { | |||||||
|             ptr.write(*dirent); |             ptr.write(*dirent); | ||||||
|             let name_ptr = ptr.add(1) as _; |             let name_ptr = ptr.add(1) as _; | ||||||
|             write_cstr(name_ptr, name); |             write_cstr(name_ptr, name); | ||||||
|  |             if T::at_the_end_of_linux_dirent() { | ||||||
|  |                 // pad zero bytes and file type at the end
 | ||||||
|  |                 let mut ptr = name_ptr.add(name.len() + 1); | ||||||
|  |                 let mut rest_len = { | ||||||
|  |                     let written_len = core::mem::size_of::<LinuxDirent<T>>() + name.len() + 1; | ||||||
|  |                     dirent.len() - written_len | ||||||
|  |                 }; | ||||||
|  |                 while rest_len > 1 { | ||||||
|  |                     ptr.write(0); | ||||||
|  |                     ptr = ptr.add(1); | ||||||
|  |                     rest_len -= 1; | ||||||
|  |                 } | ||||||
|  |                 // the last one is file type
 | ||||||
|  |                 ptr.write(DT_UNKNOWN); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         self.rest_size -= dirent.len(); |         self.rest_size -= dirent.len(); | ||||||
|         self.written_size += dirent.len(); |         self.written_size += dirent.len(); | ||||||
|  | |||||||
| @ -7,6 +7,38 @@ | |||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include "test_fs.h" | #include "test_fs.h" | ||||||
| 
 | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Helper function
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | #define NUM 9 | ||||||
|  | static bool check_dir_entries(char entries[][256], int entry_cnt) { | ||||||
|  |     char *expected_entries[NUM] = { | ||||||
|  |         "bin", | ||||||
|  |         "dev", | ||||||
|  |         "host", | ||||||
|  |         "lib", | ||||||
|  |         "lib64", | ||||||
|  |         "proc", | ||||||
|  |         "opt", | ||||||
|  |         "root", | ||||||
|  |         "tmp", | ||||||
|  |     }; | ||||||
|  |     for (int i = 0; i < NUM; i++) { | ||||||
|  |         bool find_entry = false; | ||||||
|  |         for (int j = 0; j < entry_cnt; j++) { | ||||||
|  |             if (strncmp(expected_entries[i], entries[j], strlen(expected_entries[i])) == 0) { | ||||||
|  |                 find_entry = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!find_entry) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ============================================================================
 | // ============================================================================
 | ||||||
| // The test case of readdir
 | // The test case of readdir
 | ||||||
| // ============================================================================
 | // ============================================================================
 | ||||||
| @ -14,21 +46,28 @@ | |||||||
| static int test_readdir() { | static int test_readdir() { | ||||||
|     struct dirent *dp; |     struct dirent *dp; | ||||||
|     DIR *dirp; |     DIR *dirp; | ||||||
|  |     char entries[32][256] = { 0 }; | ||||||
| 
 | 
 | ||||||
|     dirp = opendir("/"); |     dirp = opendir("/"); | ||||||
|     if (dirp == NULL) { |     if (dirp == NULL) { | ||||||
|         THROW_ERROR("failed to open directory"); |         THROW_ERROR("failed to open directory"); | ||||||
|     } |     } | ||||||
|  |     int entry_cnt = 0; | ||||||
|     while (1) { |     while (1) { | ||||||
|         errno = 0; |         errno = 0; | ||||||
|         dp = readdir(dirp); |         dp = readdir(dirp); | ||||||
|         if (dp == NULL) { |         if (dp == NULL) { | ||||||
|             if (errno != 0) { |             if (errno != 0) { | ||||||
|                 closedir(dirp); |                 closedir(dirp); | ||||||
|                 THROW_ERROR("faild to call readdir"); |                 THROW_ERROR("failed to call readdir"); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |         strncpy(entries[entry_cnt], dp->d_name, 256); | ||||||
|  |         ++entry_cnt; | ||||||
|  |     } | ||||||
|  |     if (!check_dir_entries(entries, entry_cnt)) { | ||||||
|  |         THROW_ERROR("failed to check the result of readdir"); | ||||||
|     } |     } | ||||||
|     closedir(dirp); |     closedir(dirp); | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user