3535#include "lib/fatfs/diskio.h"
3636#include "extmod/vfs_fat_file.h"
3737#include "extmod/fsusermount.h"
38+ #include "timeutils.h"
3839
3940#define mp_obj_fat_vfs_t fs_user_mount_t
4041
@@ -152,6 +153,94 @@ STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
152153}
153154STATIC MP_DEFINE_CONST_FUN_OBJ_1 (fat_vfs_getcwd_obj , fat_vfs_getcwd );
154155
156+ // Checks for path equality, ignoring trailing slashes:
157+ // path_equal(/, /) -> true
158+ // second argument must be in canonical form (meaning no trailing slash, unless it's just /)
159+ STATIC bool path_equal (const char * path , const char * path_canonical ) {
160+ while (* path_canonical != '\0' && * path == * path_canonical ) {
161+ ++ path ;
162+ ++ path_canonical ;
163+ }
164+ if (* path_canonical != '\0' ) {
165+ return false;
166+ }
167+ while (* path == '/' ) {
168+ ++ path ;
169+ }
170+ return * path == '\0' ;
171+ }
172+
173+ /// \function stat(path)
174+ /// Get the status of a file or directory.
175+ STATIC mp_obj_t fat_vfs_stat (mp_obj_t vfs_in , mp_obj_t path_in ) {
176+ const char * path = mp_obj_str_get_str (path_in );
177+
178+ FILINFO fno ;
179+ #if _USE_LFN
180+ fno .lfname = NULL ;
181+ fno .lfsize = 0 ;
182+ #endif
183+ FRESULT res ;
184+
185+ if (path_equal (path , "/" )) {
186+ // stat root directory
187+ fno .fsize = 0 ;
188+ fno .fdate = 0 ;
189+ fno .ftime = 0 ;
190+ fno .fattrib = AM_DIR ;
191+ } else {
192+ res = FR_NO_PATH ;
193+ for (size_t i = 0 ; i < MP_ARRAY_SIZE (MP_STATE_PORT (fs_user_mount )); ++ i ) {
194+ fs_user_mount_t * vfs = MP_STATE_PORT (fs_user_mount )[i ];
195+ if (vfs != NULL && path_equal (path , vfs -> str )) {
196+ // stat mounted device directory
197+ fno .fsize = 0 ;
198+ fno .fdate = 0 ;
199+ fno .ftime = 0 ;
200+ fno .fattrib = AM_DIR ;
201+ res = FR_OK ;
202+ }
203+ }
204+ if (res == FR_NO_PATH ) {
205+ // stat normal file
206+ res = f_stat (path , & fno );
207+ }
208+ if (res != FR_OK ) {
209+ nlr_raise (mp_obj_new_exception_arg1 (& mp_type_OSError ,
210+ MP_OBJ_NEW_SMALL_INT (fresult_to_errno_table [res ])));
211+ }
212+ }
213+
214+ mp_obj_tuple_t * t = MP_OBJ_TO_PTR (mp_obj_new_tuple (10 , NULL ));
215+ mp_int_t mode = 0 ;
216+ if (fno .fattrib & AM_DIR ) {
217+ mode |= 0x4000 ; // stat.S_IFDIR
218+ } else {
219+ mode |= 0x8000 ; // stat.S_IFREG
220+ }
221+ mp_int_t seconds = timeutils_seconds_since_2000 (
222+ 1980 + ((fno .fdate >> 9 ) & 0x7f ),
223+ (fno .fdate >> 5 ) & 0x0f ,
224+ fno .fdate & 0x1f ,
225+ (fno .ftime >> 11 ) & 0x1f ,
226+ (fno .ftime >> 5 ) & 0x3f ,
227+ 2 * (fno .ftime & 0x1f )
228+ );
229+ t -> items [0 ] = MP_OBJ_NEW_SMALL_INT (mode ); // st_mode
230+ t -> items [1 ] = MP_OBJ_NEW_SMALL_INT (0 ); // st_ino
231+ t -> items [2 ] = MP_OBJ_NEW_SMALL_INT (0 ); // st_dev
232+ t -> items [3 ] = MP_OBJ_NEW_SMALL_INT (0 ); // st_nlink
233+ t -> items [4 ] = MP_OBJ_NEW_SMALL_INT (0 ); // st_uid
234+ t -> items [5 ] = MP_OBJ_NEW_SMALL_INT (0 ); // st_gid
235+ t -> items [6 ] = MP_OBJ_NEW_SMALL_INT (fno .fsize ); // st_size
236+ t -> items [7 ] = MP_OBJ_NEW_SMALL_INT (seconds ); // st_atime
237+ t -> items [8 ] = MP_OBJ_NEW_SMALL_INT (seconds ); // st_mtime
238+ t -> items [9 ] = MP_OBJ_NEW_SMALL_INT (seconds ); // st_ctime
239+
240+ return MP_OBJ_FROM_PTR (t );
241+ }
242+ STATIC MP_DEFINE_CONST_FUN_OBJ_2 (fat_vfs_stat_obj , fat_vfs_stat );
243+
155244STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table [] = {
156245 { MP_ROM_QSTR (MP_QSTR_mkfs ), MP_ROM_PTR (& fat_vfs_mkfs_obj ) },
157246 { MP_ROM_QSTR (MP_QSTR_open ), MP_ROM_PTR (& fat_vfs_open_obj ) },
@@ -161,6 +250,7 @@ STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
161250 { MP_ROM_QSTR (MP_QSTR_getcwd ), MP_ROM_PTR (& fat_vfs_getcwd_obj ) },
162251 { MP_ROM_QSTR (MP_QSTR_remove ), MP_ROM_PTR (& fat_vfs_remove_obj ) },
163252 { MP_ROM_QSTR (MP_QSTR_rename ), MP_ROM_PTR (& fat_vfs_rename_obj ) },
253+ { MP_ROM_QSTR (MP_QSTR_stat ), MP_ROM_PTR (& fat_vfs_stat_obj ) },
164254};
165255STATIC MP_DEFINE_CONST_DICT (fat_vfs_locals_dict , fat_vfs_locals_dict_table );
166256
0 commit comments