uint fat_write_block(char* name, uint offset, uchar* buffer){ name = fat_name_conv(name); fat_dir_t* entry = fat_search(name); if(entry == NULL) return (uint) -1; ushort cluster = entry->cluster_low; if(cluster >= 0xFFF0){ //If the cluster is the end, make a new one cluster = fat_next_cluster(); entry->cluster_low = cluster; cluster_list[cluster] = 0xFFFF; } while(offset != 0){ if(cluster_list[cluster] >= 0xFFF0){ //End? Make a new one and continue cluster_list[cluster] = fat_next_cluster(); cluster_list[cluster_list[cluster]] = 0xFFFF; } cluster = cluster_list[cluster]; offset--; } ata_write_blocks((ushort*) buffer, cluster_to_lba(cluster), FAT_SPC); return 0; }
/* get the next sector in the current file. */ int fat_next_sector(int fd) { int c; int rerrno; #ifdef TRACE printf("fat_next_sector(%d)\n", fd); #endif /* if the current sector was written write to disc */ if(fat_flush(fd)) { return -1; } /* see if we need another cluster */ if(file_num[fd].sectors_left > 0) { file_num[fd].sectors_left--; file_num[fd].file_sector++; file_num[fd].cursor = 0; return block_read(++file_num[fd].sector, file_num[fd].buffer); } else { c = fat_next_cluster(fd, &rerrno); if(c > -1) { file_num[fd].file_sector++; return fat_select_cluster(fd, c); } else { return -1; } } }
/* * Find empty directory entry and put new entry on it. * This search is done only in directory of specified cluster. * @dvp: vnode for directory. * @np: pointer to fat node */ int fatfs_add_node(vnode_t dvp, struct fatfs_node *np) { struct fatfsmount *fmp; u_long cl, sec, sec_start; int err; u_long i, next; fmp = (struct fatfsmount *)dvp->v_mount->m_data; cl = dvp->v_blkno; DPRINTF(("fatfs_add_node: cl=%d\n", cl)); if (cl == CL_ROOT && !(FAT32(fmp))) { /* Add entry in root directory */ sec_start = fmp->root_start; for (sec = sec_start; sec < fmp->data_start; sec++) { err = fat_add_dirent(fmp, sec, np); if (err != ENOENT) return err; } } else { /* Search entry in sub directory */ if(cl == CL_ROOT) /* CL_ROOT of FAT32 */ cl = fmp->root_start; while (!IS_EOFCL(fmp, cl)) { sec = cl_to_sec(fmp, cl); for (i = 0; i < fmp->sec_per_cl; i++) { err = fat_add_dirent(fmp, sec, np); if (err != ENOENT) return err; sec++; } err = fat_next_cluster(fmp, cl, &next); if (err) return err; cl = next; } /* No entry found, add one more free cluster for directory */ DPRINTF(("fatfs_add_node: expand dir\n")); err = fat_expand_dir(fmp, cl, &next); if (err) return err; /* Initialize free cluster. */ memset(fmp->dir_buf, 0, SEC_SIZE); sec = cl_to_sec(fmp, next); for (i = 0; i < fmp->sec_per_cl; i++) { err = fat_write_dirent(fmp, sec); if (err) return err; sec++; } /* Try again */ sec = cl_to_sec(fmp, next); err = fat_add_dirent(fmp, sec, np); return err; } return ENOENT; }
/* * Find directory entry for specified name in directory. * The fat vnode data is filled if success. * * @dvp: vnode for directory. * @name: file name * @np: pointer to fat node */ int fatfs_lookup_node(vnode_t dvp, char *name, struct fatfs_node *np) { struct fatfsmount *fmp; char fat_name[12]; u_long i, cl, sec, sec_start; int err; if (name == NULL) return ENOENT; DPRINTF(("fat_lookup_denode: cl=%d name=%s\n", dvp->v_blkno, name)); fat_convert_name(name, fat_name); *(fat_name + 11) = '\0'; fmp = (struct fatfsmount *)dvp->v_mount->m_data; cl = dvp->v_blkno; if (cl == CL_ROOT && !(FAT32(fmp))) { /* Search entry in root directory */ sec_start = fmp->root_start; for (sec = sec_start; sec < fmp->data_start; sec++) { err = fat_lookup_dirent(fmp, sec, fat_name, np); if (err != EAGAIN) return err; } } else { /* Search entry in sub directory */ if(cl == CL_ROOT) /* CL_ROOT of FAT32 */ cl = fmp->root_start; while (!IS_EOFCL(fmp, cl)) { sec = cl_to_sec(fmp, cl); for (i = 0; i < fmp->sec_per_cl; i++) { err = fat_lookup_dirent(fmp, sec, fat_name, np); if (err != EAGAIN) return err; sec++; } err = fat_next_cluster(fmp, cl, &cl); if (err) return err; } } return ENOENT; }
/* * Get directory entry for specified index. * * @dvp: vnode for directory. * @index: index of the entry * @np: pointer to fat node */ int fatfs_get_node(vnode_t dvp, int index, struct fatfs_node *np) { struct fatfsmount *fmp; u_long i, cl, sec, sec_start; int cur_index, err; fmp = (struct fatfsmount *)dvp->v_mount->m_data; cl = dvp->v_blkno; cur_index = 0; DPRINTF(("fatfs_get_node: index=%d\n", index)); if (cl == CL_ROOT && !(FAT32(fmp))) { /* Get entry from the root directory */ sec_start = fmp->root_start; for (sec = sec_start; sec < fmp->data_start; sec++) { err = fat_get_dirent(fmp, sec, index, &cur_index, np); if (err != EAGAIN) return err; } } else { if(cl == CL_ROOT) /* CL_ROOT of FAT32 */ cl = fmp->root_start; /* Get entry from the sub directory */ while (!IS_EOFCL(fmp, cl)) { sec = cl_to_sec(fmp, cl); for (i = 0; i < fmp->sec_per_cl; i++) { err = fat_get_dirent(fmp, sec, index, &cur_index, np); if (err != EAGAIN) return err; sec++; } err = fat_next_cluster(fmp, cl, &cl); if (err) return err; } } return ENOENT; }
int fat_lseek(int fd, int ptr, int dir, int *rerrno) { unsigned int new_pos; unsigned int old_pos; int new_sec; int i; int file_cluster; (*rerrno) = 0; if(fd >= MAX_OPEN_FILES) { (*rerrno) = EBADF; return ptr-1; } if(!(file_num[fd].flags & FAT_FLAG_OPEN)) { (*rerrno) = EBADF; return ptr-1; /* tried to seek on a file that's not open */ } fat_flush(fd); old_pos = file_num[fd].file_sector * 512 + file_num[fd].cursor; if(dir == SEEK_SET) { new_pos = ptr; } else if(dir == SEEK_CUR) { new_pos = file_num[fd].file_sector * 512 + file_num[fd].cursor + ptr; } else { new_pos = file_num[fd].size + ptr; } if(new_pos > file_num[fd].size) { return ptr-1; /* tried to seek outside a file */ } // optimisation cases if((old_pos/512) == (new_pos/512)) { // case 1: seeking within a disk block file_num[fd].cursor = new_pos & 0x1ff; return new_pos; } else if((new_pos / (fatfs.sectors_per_cluster * 512)) == (old_pos / (fatfs.sectors_per_cluster * 512))) { // case 2: seeking within the cluster, just need to hop forward/back some sectors file_num[fd].file_sector = new_pos / 512; file_num[fd].sector = file_num[fd].sector + (new_pos/512) - (old_pos/512); file_num[fd].sectors_left = file_num[fd].sectors_left + (new_pos/512) - (old_pos/512); file_num[fd].cursor = new_pos & 0x1ff; if(block_read(file_num[fd].sector, file_num[fd].buffer)) { return ptr - 1; } return new_pos; } // otherwise we need to seek the cluster chain file_cluster = new_pos / (fatfs.sectors_per_cluster * 512); file_num[fd].cluster = file_num[fd].full_first_cluster; i = 0; // walk the FAT cluster chain until we get to the right one while(i<file_cluster) { file_num[fd].cluster = fat_next_cluster(fd, rerrno); i++; } file_num[fd].file_sector = new_pos / 512; file_num[fd].cursor = new_pos & 0x1ff; new_sec = new_pos - file_cluster * fatfs.sectors_per_cluster * 512; new_sec = new_sec / 512; file_num[fd].sector = file_num[fd].cluster * fatfs.sectors_per_cluster + fatfs.cluster0 + new_sec; file_num[fd].sectors_left = fatfs.sectors_per_cluster - new_sec - 1; if(block_read(file_num[fd].sector, file_num[fd].buffer)) { return ptr-1; } return new_pos; }
static int fatfs_write(vnode_t vp, file_t fp, void *buf, size_t size, size_t *result) { struct fatfsmount *fmp; struct fatfs_node *np; struct fat_dirent *de; int nr_copy, nr_write, buf_pos, i, cl_size, error; u_long file_pos, end_pos; u_long cl; DPRINTF(("fatfs_write: vp=%x size=%d\n", vp, size)); *result = 0; fmp = vp->v_mount->m_data; if (vp->v_type == VDIR) return EISDIR; if (vp->v_type != VREG) return EINVAL; mutex_lock(&fmp->lock); /* Check if file position exceeds the end of file. */ end_pos = vp->v_size; file_pos = (fp->f_flags & O_APPEND) ? end_pos : fp->f_offset; if (file_pos + size > end_pos) { /* Expand the file size before writing to it */ end_pos = file_pos + size; error = fat_expand_file(fmp, vp->v_blkno, end_pos); if (error) { error = EIO; goto out; } /* Update directory entry */ np = vp->v_data; de = &np->dirent; de->size = end_pos; error = fatfs_put_node(fmp, np); if (error) goto out; vp->v_size = end_pos; } /* Seek to the cluster for the file offset */ error = fat_seek_cluster(fmp, vp->v_blkno, file_pos, &cl); if (error) goto out; buf_pos = file_pos % fmp->cluster_size; cl_size = size / fmp->cluster_size + 1; nr_write = 0; i = 0; do { /* First and last cluster must be read before write */ if (i == 0 || i == cl_size) { if (fat_read_cluster(fmp, cl)) { error = EIO; goto out; } } nr_copy = fmp->cluster_size; if (buf_pos > 0) nr_copy -= buf_pos; if (buf_pos + size < fmp->cluster_size) nr_copy = size; memcpy(fmp->io_buf + buf_pos, buf, nr_copy); if (fat_write_cluster(fmp, cl)) { error = EIO; goto out; } file_pos += nr_copy; nr_write += nr_copy; size -= nr_copy; if (size <= 0) break; error = fat_next_cluster(fmp, cl, &cl); if (error) goto out; buf = (void *)((u_long)buf + nr_copy); buf_pos = 0; i++; } while (!IS_EOFCL(fmp, cl)); fp->f_offset = file_pos; /* * XXX: Todo! * de.time = ? * de.date = ? * if (dirent_set(fp, &de)) * return EIO; */ *result = nr_write; error = 0; out: mutex_unlock(&fmp->lock); return error; }
static int fatfs_read(vnode_t vp, file_t fp, void *buf, size_t size, size_t *result) { struct fatfsmount *fmp; int nr_read, nr_copy, buf_pos, error; u_long cl, file_pos; DPRINTF(("fatfs_read: vp=%x\n", vp)); *result = 0; fmp = vp->v_mount->m_data; if (vp->v_type == VDIR) return EISDIR; if (vp->v_type != VREG) return EINVAL; /* Check if current file position is already end of file. */ file_pos = fp->f_offset; if (file_pos >= vp->v_size) return 0; mutex_lock(&fmp->lock); /* Get the actual read size. */ if (vp->v_size - file_pos < size) size = vp->v_size - file_pos; /* Seek to the cluster for the file offset */ error = fat_seek_cluster(fmp, vp->v_blkno, file_pos, &cl); if (error) goto out; /* Read and copy data */ nr_read = 0; buf_pos = file_pos % fmp->cluster_size; do { if (fat_read_cluster(fmp, cl)) { error = EIO; goto out; } nr_copy = fmp->cluster_size; if (buf_pos > 0) nr_copy -= buf_pos; if (buf_pos + size < fmp->cluster_size) nr_copy = size; memcpy(buf, fmp->io_buf + buf_pos, nr_copy); file_pos += nr_copy; nr_read += nr_copy; size -= nr_copy; if (size <= 0) break; error = fat_next_cluster(fmp, cl, &cl); if (error) goto out; buf = (void *)((u_long)buf + nr_copy); buf_pos = 0; } while (!IS_EOFCL(fmp, cl)); fp->f_offset = file_pos; *result = nr_read; error = 0; out: mutex_unlock(&fmp->lock); return error; }