My Project
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules
sdmc_dev.c
Go to the documentation of this file.
1 #include <fcntl.h>
2 #include <errno.h>
3 #include <unistd.h>
4 #include <sys/iosupport.h>
5 #include <3ds.h>
6 
14 static int sdmc_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
15 static int sdmc_close(struct _reent *r, int fd);
16 static ssize_t sdmc_write(struct _reent *r, int fd, const char *ptr, size_t len);
17 static ssize_t sdmc_read(struct _reent *r, int fd, char *ptr, size_t len);
18 static off_t sdmc_seek(struct _reent *r, int fd, off_t pos, int dir);
19 static int sdmc_fstat(struct _reent *r, int fd, struct stat *st);
20 static int sdmc_stat(struct _reent *r, const char *file, struct stat *st);
21 static int sdmc_link(struct _reent *r, const char *existing, const char *newLink);
22 static int sdmc_unlink(struct _reent *r, const char *name);
23 static int sdmc_chdir(struct _reent *r, const char *name);
24 static int sdmc_rename(struct _reent *r, const char *oldName, const char *newName);
25 static int sdmc_mkdir(struct _reent *r, const char *path, int mode);
26 static DIR_ITER* sdmc_diropen(struct _reent *r, DIR_ITER *dirState, const char *path);
27 static int sdmc_dirreset(struct _reent *r, DIR_ITER *dirState);
28 static int sdmc_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
29 static int sdmc_dirclose(struct _reent *r, DIR_ITER *dirState);
30 static int sdmc_statvfs(struct _reent *r, const char *path, struct statvfs *buf);
31 static int sdmc_ftruncate(struct _reent *r, int fd, off_t len);
32 static int sdmc_fsync(struct _reent *r, int fd);
33 static int sdmc_chmod(struct _reent *r, const char *path, mode_t mode);
34 static int sdmc_fchmod(struct _reent *r, int fd, mode_t mode);
35 
39 typedef struct
40 {
41  Handle fd;
42  int flags;
43  u64 offset;
44 } sdmc_file_t;
45 
47 typedef struct
48 {
49  Handle fd;
50  FS_dirent entry_data;
51 } sdmc_dir_t;
52 
54 static devoptab_t
55 sdmc_devoptab =
56 {
57  .name = "sdmc",
58  .structSize = sizeof(sdmc_file_t),
59  .open_r = sdmc_open,
60  .close_r = sdmc_close,
61  .write_r = sdmc_write,
62  .read_r = sdmc_read,
63  .seek_r = sdmc_seek,
64  .fstat_r = sdmc_fstat,
65  .stat_r = sdmc_stat,
66  .link_r = sdmc_link,
67  .unlink_r = sdmc_unlink,
68  .chdir_r = sdmc_chdir,
69  .rename_r = sdmc_rename,
70  .mkdir_r = sdmc_mkdir,
71  .dirStateSize = sizeof(sdmc_dir_t),
72  .diropen_r = sdmc_diropen,
73  .dirreset_r = sdmc_dirreset,
74  .dirnext_r = sdmc_dirnext,
75  .dirclose_r = sdmc_dirclose,
76  .statvfs_r = sdmc_statvfs,
77  .ftruncate_r = sdmc_ftruncate,
78  .fsync_r = sdmc_fsync,
79  .deviceData = NULL,
80  .chmod_r = sdmc_chmod,
81  .fchmod_r = sdmc_fchmod,
82 };
83 
85 static FS_archive sdmcArchive =
86 {
87  .id = ARCH_SDMC,
88  .lowPath =
89  {
90  .type = PATH_EMPTY,
91  .size = 1,
92  .data = (u8*)"",
93  },
94 };
95 
100 {
101  Result rc;
102 
103  rc = FSUSER_OpenArchive(NULL, &sdmcArchive);
104  if(rc == 0)
105  AddDevice(&sdmc_devoptab);
106 
107  return rc;
108 }
109 
112 {
113  Result rc;
114 
115  rc = FSUSER_CloseArchive(NULL, &sdmcArchive);
116  if(rc == 0)
117  RemoveDevice("sdmc");
118 
119  return rc;
120 }
121 
133 static int
134 sdmc_open(struct _reent *r,
135  void *fileStruct,
136  const char *path,
137  int flags,
138  int mode)
139 {
140  Handle fd;
141  Result rc;
142  u32 sdmc_flags = 0;
143  u32 attributes = FS_ATTRIBUTE_NONE;
144  char *pathptr = NULL;
145 
146  pathptr = strchr(path, '/');
147  if(pathptr==NULL)pathptr = (char*)path;
148 
149  /* get pointer to our data */
150  sdmc_file_t *file = (sdmc_file_t*)fileStruct;
151 
152  /* check access mode */
153  switch(flags & O_ACCMODE)
154  {
155  /* read-only: do not allow O_APPEND */
156  case O_RDONLY:
157  sdmc_flags |= FS_OPEN_READ;
158  if(flags & O_APPEND)
159  {
160  r->_errno = EINVAL;
161  return -1;
162  }
163  break;
164 
165  /* write-only */
166  case O_WRONLY:
167  sdmc_flags |= FS_OPEN_WRITE;
168  break;
169 
170  /* read and write */
171  case O_RDWR:
172  sdmc_flags |= (FS_OPEN_READ | FS_OPEN_WRITE);
173  break;
174 
175  /* an invalid option was supplied */
176  default:
177  r->_errno = EINVAL;
178  return -1;
179  }
180 
181  /* create file */
182  if(flags & O_CREAT)
183  sdmc_flags |= FS_OPEN_CREATE;
184 
185  /* TODO: Test O_EXCL. */
186 
187  /* set attributes */
188  /*if(!(mode & S_IWUSR))
189  attributes |= FS_ATTRIBUTE_READONLY;*/
190 
191  /* open the file */
192  rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr),
193  sdmc_flags, attributes);
194  if(rc == 0)
195  {
196  file->fd = fd;
197  file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC));
198  file->offset = 0;
199  return 0;
200  }
201 
202  r->_errno = rc;
203  return -1;
204 }
205 
214 static int
215 sdmc_close(struct _reent *r,
216  int fd)
217 {
218  Result rc;
219 
220  /* get pointer to our data */
221  sdmc_file_t *file = (sdmc_file_t*)fd;
222 
223  rc = FSFILE_Close(file->fd);
224  if(rc == 0)
225  return 0;
226 
227  r->_errno = rc;
228  return -1;
229 }
230 
241 static ssize_t
242 sdmc_write(struct _reent *r,
243  int fd,
244  const char *ptr,
245  size_t len)
246 {
247  Result rc;
248  u32 bytes;
249  u32 sync = 0;
250  u64 offset;
251 
252  /* get pointer to our data */
253  sdmc_file_t *file = (sdmc_file_t*)fd;
254 
255  /* check that the file was opened with write access */
256  if((file->flags & O_ACCMODE) == O_RDONLY)
257  {
258  r->_errno = EBADF;
259  return -1;
260  }
261 
262  /* check if this is synchronous or not */
263  if(file->flags & O_SYNC)
264  sync = 0x10001;
265 
266  /* initialize offset */
267  offset = file->offset;
268  if(file->flags & O_APPEND)
269  {
270  /* append means write from the end of the file */
271  rc = FSFILE_GetSize(file->fd, &offset);
272  if(rc != 0)
273  {
274  r->_errno = rc;
275  return -1;
276  }
277  }
278 
279  /* TODO: Copy to internal buffer and write in chunks.
280  * You cannot write from read-only memory.
281  */
282 
283  /* write the data */
284  rc = FSFILE_Write(file->fd, &bytes, offset, (u32*)ptr, (u32)len, sync);
285  if(rc == 0)
286  {
287  /* update current file offset; if O_APPEND, this moves it to the
288  * new end-of-file
289  */
290  file->offset = offset + bytes;
291  return (ssize_t)bytes;
292  }
293 
294  r->_errno = rc;
295  return -1;
296 }
297 
308 static ssize_t
309 sdmc_read(struct _reent *r,
310  int fd,
311  char *ptr,
312  size_t len)
313 {
314  Result rc;
315  u32 bytes;
316 
317  /* get pointer to our data */
318  sdmc_file_t *file = (sdmc_file_t*)fd;
319 
320  /* check that the file was opened with read access */
321  if((file->flags & O_ACCMODE) == O_WRONLY)
322  {
323  r->_errno = EBADF;
324  return -1;
325  }
326 
327  /* read the data */
328  rc = FSFILE_Read(file->fd, &bytes, file->offset, (u32*)ptr, (u32)len);
329  if(rc == 0)
330  {
331  /* update current file offset */
332  file->offset += bytes;
333  return (ssize_t)bytes;
334  }
335 
336  r->_errno = rc;
337  return -1;
338 }
339 
350 static off_t
351 sdmc_seek(struct _reent *r,
352  int fd,
353  off_t pos,
354  int whence)
355 {
356  Result rc;
357  u64 offset;
358 
359  /* get pointer to our data */
360  sdmc_file_t *file = (sdmc_file_t*)fd;
361 
362  /* find the offset to see from */
363  switch(whence)
364  {
365  /* set absolute position; start offset is 0 */
366  case SEEK_SET:
367  offset = 0;
368  break;
369 
370  /* set position relative to the current position */
371  case SEEK_CUR:
372  offset = file->offset;
373  break;
374 
375  /* set position relative to the end of the file */
376  case SEEK_END:
377  rc = FSFILE_GetSize(file->fd, &offset);
378  if(rc != 0)
379  {
380  r->_errno = rc;
381  return -1;
382  }
383  break;
384 
385  /* an invalid option was provided */
386  default:
387  r->_errno = EINVAL;
388  return -1;
389  }
390 
391  /* TODO: A better check that prevents overflow. */
392  if(pos < 0 && offset < -pos)
393  {
394  /* don't allow seek to before the beginning of the file */
395  r->_errno = EINVAL;
396  return -1;
397  }
398 
399  /* update the current offset */
400  file->offset = offset + pos;
401  return file->offset;
402 }
403 
413 static int
414 sdmc_fstat(struct _reent *r,
415  int fd,
416  struct stat *st)
417 {
418  r->_errno = ENOSYS;
419  return -1;
420 }
421 
431 static int
432 sdmc_stat(struct _reent *r,
433  const char *file,
434  struct stat *st)
435 {
436  r->_errno = ENOSYS;
437  return -1;
438 }
439 
449 static int
450 sdmc_link(struct _reent *r,
451  const char *existing,
452  const char *newLink)
453 {
454  r->_errno = ENOSYS;
455  return -1;
456 }
457 
466 static int
467 sdmc_unlink(struct _reent *r,
468  const char *name)
469 {
470  r->_errno = ENOSYS;
471  return -1;
472 }
473 
482 static int
483 sdmc_chdir(struct _reent *r,
484  const char *name)
485 {
486  r->_errno = ENOSYS;
487  return -1;
488 }
489 
499 static int
500 sdmc_rename(struct _reent *r,
501  const char *oldName,
502  const char *newName)
503 {
504  r->_errno = ENOSYS;
505  return -1;
506 }
507 
517 static int
518 sdmc_mkdir(struct _reent *r,
519  const char *path,
520  int mode)
521 {
522  Result rc;
523  char *pathptr = NULL;
524 
525  pathptr = strchr(path, '/');
526  if(pathptr==NULL)pathptr = (char*)path;
527 
528  /* TODO: Use mode to set directory attributes. */
529 
530  rc = FSUSER_CreateDirectory(NULL, sdmcArchive, FS_makePath(PATH_CHAR, pathptr));
531  if(rc == 0)
532  return 0;
533 
534  r->_errno = ENOSYS;
535  return -1;
536 }
537 
547 static DIR_ITER*
548 sdmc_diropen(struct _reent *r,
549  DIR_ITER *dirState,
550  const char *path)
551 {
552  Handle fd;
553  Result rc;
554  char *pathptr = NULL;
555 
556  pathptr = strchr(path, '/');
557  if(pathptr==NULL)pathptr = (char*)path;
558 
559  /* get pointer to our data */
560  sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct);
561 
562  /* open the directory */
563  rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr));
564  if(rc == 0)
565  {
566  dir->fd = fd;
567  memset(&dir->entry_data, 0, sizeof(dir->entry_data));
568  return dirState;
569  }
570 
571  r->_errno = rc;
572  return NULL;
573 }
574 
583 static int
584 sdmc_dirreset(struct _reent *r,
585  DIR_ITER *dirState)
586 {
587  r->_errno = ENOSYS;
588  return -1;
589 }
590 
601 static int
602 sdmc_dirnext(struct _reent *r,
603  DIR_ITER *dirState,
604  char *filename,
605  struct stat *filestat)
606 {
607  Result rc;
608  u32 entries;
609  u16 *name;
610 
611  /* get pointer to our data */
612  sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct);
613 
614  /* fetch the next entry */
615  rc = FSDIR_Read(dir->fd, &entries, 1, &dir->entry_data);
616  if(rc == 0)
617  {
618  if(entries == 0)
619  {
620  /* there are no more entries; ENOENT signals end-of-directory */
621  r->_errno = ENOENT;
622  return -1;
623  }
624 
625  /* fill in the stat info */
626  filestat->st_ino = 0;
627  if(dir->entry_data.isDirectory)
628  filestat->st_mode = S_IFDIR;
629  else
630  filestat->st_mode = S_IFREG;
631 
632  /* copy the name */
633  name = dir->entry_data.name;
634  while(*name)
635  *filename++ = *name++;
636  *filename = 0;
637 
638  return 0;
639  }
640 
641  r->_errno = rc;
642  return -1;
643 }
644 
653 static int
654 sdmc_dirclose(struct _reent *r,
655  DIR_ITER *dirState)
656 {
657  Result rc;
658 
659  /* get pointer to our data */
660  sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct);
661 
662  /* close the directory */
663  rc = FSDIR_Close(dir->fd);
664  if(rc == 0)
665  return 0;
666 
667  r->_errno = rc;
668  return -1;
669 }
670 
680 static int
681 sdmc_statvfs(struct _reent *r,
682  const char *path,
683  struct statvfs *buf)
684 {
685  Result rc;
686  u32 clusterSize, numClusters, freeClusters;
687  u32 writable = 0;
688 
690  NULL,
691  &clusterSize,
692  &numClusters,
693  &freeClusters);
694 
695  if(rc == 0)
696  {
697  buf->f_bsize = clusterSize;
698  buf->f_frsize = clusterSize;
699  buf->f_blocks = numClusters;
700  buf->f_bfree = freeClusters;
701  buf->f_bavail = freeClusters;
702  buf->f_files = 0; //??? how to get
703  buf->f_ffree = freeClusters;
704  buf->f_favail = freeClusters;
705  buf->f_fsid = 0; //??? how to get
706  buf->f_flag = ST_NOSUID;
707  buf->f_namemax = 0; //??? how to get
708 
709  rc = FSUSER_IsSdmcWritable(NULL, &writable);
710  if(rc != 0 || !writable)
711  buf->f_flag |= ST_RDONLY;
712 
713  return 0;
714  }
715 
716  r->_errno = rc;
717  return -1;
718 }
719 
729 static int
730 sdmc_ftruncate(struct _reent *r,
731  int fd,
732  off_t len)
733 {
734  Result rc;
735 
736  /* get pointer to our data */
737  sdmc_file_t *file = (sdmc_file_t*)fd;
738 
739  /* make sure length is non-negative */
740  if(len < 0)
741  {
742  r->_errno = EINVAL;
743  return -1;
744  }
745 
746  /* set the new file size */
747  rc = FSFILE_SetSize(file->fd, len);
748  if(rc == 0)
749  return 0;
750 
751  r->_errno = rc;
752  return -1;
753 }
754 
763 static int
764 sdmc_fsync(struct _reent *r,
765  int fd)
766 {
767  Result rc;
768 
769  /* get pointer to our data */
770  sdmc_file_t *file = (sdmc_file_t*)fd;
771 
772  rc = FSFILE_Flush(file->fd);
773  if(rc == 0)
774  return 0;
775 
776  r->_errno = rc;
777  return -1;
778 }
779 
789 static int
790 sdmc_chmod(struct _reent *r,
791  const char *path,
792  mode_t mode)
793 {
794  r->_errno = ENOSYS;
795  return -1;
796 }
797 
807 static int
808 sdmc_fchmod(struct _reent *r,
809  int fd,
810  mode_t mode)
811 {
812  r->_errno = ENOSYS;
813  return -1;
814 }
Result FSUSER_IsSdmcWritable(Handle *handle, u32 *writable)
Definition: fs.c:869
Result FSDIR_Close(Handle handle)
Definition: fs.c:1325
Definition: fs.h:105
s32 Result
Definition: types.h:42
uint16_t u16
Definition: types.h:22
Result FSUSER_OpenArchive(Handle *handle, FS_archive *archive)
Definition: fs.c:663
Result FSDIR_Read(Handle handle, u32 *entriesRead, u32 entrycount, FS_dirent *buffer)
Definition: fs.c:1281
u32 Handle
Definition: types.h:41
Result sdmcExit(void)
Definition: sdmc_dev.c:111
Result FSFILE_Flush(Handle handle)
Definition: fs.c:1239
uint8_t u8
Definition: types.h:21
uint64_t u64
Definition: types.h:24
Result FSFILE_Close(Handle handle)
Definition: fs.c:911
Result FSFILE_SetSize(Handle handle, u64 size)
Definition: fs.c:1120
Result FSUSER_OpenDirectory(Handle *handle, Handle *out, FS_archive archive, FS_path dirLowPath)
Definition: fs.c:605
u32 id
Archive ID.
Definition: fs.h:98
Result FSFILE_GetSize(Handle handle, u64 *size)
Definition: fs.c:1078
Result FSUSER_CloseArchive(Handle *handle, FS_archive *archive)
Definition: fs.c:717
uint32_t u32
Definition: types.h:23
#define FS_OPEN_READ
Definition: fs.h:19
#define FS_OPEN_CREATE
Definition: fs.h:23
Result FSFILE_Write(Handle handle, u32 *bytesWritten, u64 offset, const void *buffer, u32 size, u32 flushFlags)
Definition: fs.c:1026
#define FS_OPEN_WRITE
Definition: fs.h:21
Result sdmcInit(void)
Definition: sdmc_dev.c:99
Result FSUSER_GetSdmcArchiveResource(Handle *handle, u32 *sectorSize, u32 *clusterSize, u32 *numClusters, u32 *freeClusters)
Definition: fs.c:769
Result FSUSER_OpenFile(Handle *handle, Handle *out, FS_archive archive, FS_path fileLowPath, u32 openflags, u32 attributes)
Definition: fs.c:130
Result FSFILE_Read(Handle handle, u32 *bytesRead, u64 offset, void *buffer, u32 size)
Definition: fs.c:959
u16 name[0x106]
UTF-16 encoded name.
Definition: fs.h:108
Result FSUSER_CreateDirectory(Handle *handle, FS_archive archive, FS_path dirLowPath)
Definition: fs.c:475
Specifies a text based path with a 8-bit byte per character.
Definition: fs.h:66
Definition: fs.h:96
Specifies an empty path.
Definition: fs.h:64
Definition: fs.h:78
#define FS_ATTRIBUTE_NONE
Definition: fs.h:35