int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName) { ntfs_log_trace("oldName %s, newName %s\n", oldName, newName); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path vd = ntfsGetVolume(oldName); if (!vd) { r->_errno = ENODEV; return -1; } // Lock ntfsLock(vd); // You cannot rename between devices if(vd != ntfsGetVolume(newName)) { ntfsUnlock(vd); r->_errno = EXDEV; return -1; } // Check that there is no existing entry with the new name ni = ntfsOpenEntry(vd, newName); if (ni) { ntfsCloseEntry(vd, ni); ntfsUnlock(vd); r->_errno = EEXIST; return -1; } // Link the old entry with the new one if (ntfsLink(vd, oldName, newName)) { ntfsUnlock(vd); return -1; } // Unlink the old entry if (ntfsUnlink(vd, oldName)) { if (ntfsUnlink(vd, newName)) { ntfsUnlock(vd); return -1; } ntfsUnlock(vd); return -1; } // Unlock ntfsUnlock(vd); return 0; }
int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); ntfs_dir_state* dir = STATE(dirState); ntfs_inode *ni = NULL; // Sanity check if (!dir || !dir->vd || !dir->ni) { r->_errno = EBADF; return -1; } // Lock ntfsLock(dir->vd); // Check that there is a entry waiting to be fetched if (!dir->current) { ntfsUnlock(dir->vd); r->_errno = ENOENT; return -1; } // Fetch the current entry strcpy(filename, dir->current->name); if(filestat != NULL) { if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0) { memset(filestat, 0, sizeof(struct stat)); filestat->st_mode = S_IFDIR; } else { ni = ntfsOpenEntry(dir->vd, dir->current->name); if (ni) { ntfsStat(dir->vd, ni, filestat); ntfsCloseEntry(dir->vd, ni); } } } // Move to the next entry in the directory dir->current = dir->current->next; // Update directory times ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Unlock ntfsUnlock(dir->vd); return 0; }
int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st) { // Short circuit cases were we don't actually have to do anything if (!st || !path) return 0; ntfs_log_trace("path %s, st %p\n", path, st); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path vd = ntfsGetVolume(path); if (!vd) { r->_errno = ENODEV; return -1; } if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) { memset(st, 0, sizeof(struct stat)); st->st_mode = S_IFDIR; return 0; } // Lock ntfsLock(vd); // Find the entry ni = ntfsOpenEntry(vd, path); if (!ni) { r->_errno = errno; ntfsUnlock(vd); return -1; } // Get the entry stats int ret = ntfsStat(vd, ni, st); if (ret) r->_errno = errno; // Close the entry ntfsCloseEntry(vd, ni); ntfsUnlock(vd); return 0; }
int ntfs_chdir_r (struct _reent *r, const char *name) { ntfs_log_trace("name %s\n", name); ntfs_vd *vd = NULL; ntfs_inode *ni = NULL; // Get the volume descriptor for this path vd = ntfsGetVolume(name); if (!vd) { r->_errno = ENODEV; return -1; } // Lock ntfsLock(vd); // Find the directory ni = ntfsOpenEntry(vd, name); if (!ni) { ntfsUnlock(vd); r->_errno = ENOENT; return -1; } // Ensure that this directory is indeed a directory if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(vd, ni); ntfsUnlock(vd); r->_errno = ENOTDIR; return -1; } // Close the old current directory (if any) if (vd->cwd_ni) ntfsCloseEntry(vd, vd->cwd_ni); // Set the new current directory vd->cwd_ni = ni; // Unlock ntfsUnlock(vd); return 0; }
int ntfs_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", (void *) fileStruct, path, flags, mode); ntfs_file_state* file = STATE(fileStruct); // Get the volume descriptor for this path file->vd = ntfsGetVolume(path); if (!file->vd) { r->_errno = ENODEV; return -1; } // Lock ntfsLock(file->vd); // Determine which mode the file is opened for file->flags = flags; if ((flags & 0x03) == O_RDONLY) { file->read = true; file->write = false; file->append = false; } else if ((flags & 0x03) == O_WRONLY) { file->read = false; file->write = true; file->append = (flags & O_APPEND); } else if ((flags & 0x03) == O_RDWR) { file->read = true; file->write = true; file->append = (flags & O_APPEND); } else { r->_errno = EACCES; ntfsUnlock(file->vd); return -1; } // Try and find the file and (if found) ensure that it is not a directory file->ni = ntfsOpenEntry(file->vd, path); if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EISDIR; return -1; } // Are we creating this file? if ((flags & O_CREAT) && !file->ni) { // Create the file file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); if (!file->ni) { ntfsUnlock(file->vd); return -1; } } // Sanity check, the file should be open by now if (!file->ni) { ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } // Open the files data attribute file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); if (!file->data_na) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); return -1; } // Determine if this files data is compressed and/or encrypted file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); // We cannot read/write encrypted files if (file->encrypted) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // Make sure we aren't trying to write to a read-only file if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EROFS; return -1; } // Truncate the file if requested if ((flags & O_TRUNC) && file->write) { if (ntfs_attr_truncate(file->data_na, 0)) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = errno; return -1; } } // Set the files current position and length file->pos = 0; file->len = file->data_na->data_size; ntfs_log_trace("file->len %llu\n", file->len); // Update file times ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); // Insert the file into the double-linked FILO list of open files if (file->vd->firstOpenFile) { file->nextOpenFile = file->vd->firstOpenFile; file->vd->firstOpenFile->prevOpenFile = file; } else { file->nextOpenFile = NULL; } file->prevOpenFile = NULL; file->vd->firstOpenFile = file; file->vd->openFileCount++; file->is_ntfs = 1; // Unlock ntfsUnlock(file->vd); return (int)(intptr_t)fileStruct; //return (int)(s64) fileStruct; }
int ntfs_file_to_sectors(struct _reent *r, const char *path, uint32_t *sec_out, uint32_t *size_out, int max, int phys) { ntfs_file_state fileStruct; ntfs_file_state* file = &fileStruct; uint32_t s_count = 0; //size_t len; off64_t len; // Get the volume descriptor for this path file->vd = ntfsGetVolume(path); if (!file->vd) { r->_errno = ENODEV; return -1; } // Lock ntfsLock(file->vd); // Try and find the file and (if found) ensure that it is not a directory file->ni = ntfsOpenEntry(file->vd, path); if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EISDIR; return -1; } // Sanity check, the file should be open by now if (!file->ni) { ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } // Open the files data attribute file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); if(!file->data_na) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); return -1; } // Determine if this files data is compressed and/or encrypted file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); // We cannot read/write encrypted files if (file->encrypted) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // Set the files current position and length file->pos = 0; len = file->len = file->data_na->data_size; struct ntfs_device *dev = file->vd->dev; gekko_fd *fd = DEV_FD(dev); while (len && s_count < max) { //size_t ret = ntfs_attr_to_sectors(file->data_na, file->pos, len, sec_out, size_out, max, &s_count, (u32) fd->sectorSize); off64_t ret = ntfs_attr_to_sectors(file->data_na, file->pos, len, sec_out, size_out, max, &s_count, (u32) fd->sectorSize); if (ret <= 0 || ret > len) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } len -= ret; file->pos += ret; } if (phys) { uint32_t i; for (i = 0; i < s_count; i++) { sec_out[i] += fd->startSector; } } ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); // Unlock ntfsUnlock(file->vd); return s_count; }
DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) { ntfs_log_trace("dirState %p, path %s\n", dirState, path); ntfs_dir_state* dir = STATE(dirState); s64 position = 0; // Get the volume descriptor for this path dir->vd = ntfsGetVolume(path); if (!dir->vd) { r->_errno = ENODEV; return NULL; } // Lock ntfsLock(dir->vd); // Find the directory dir->ni = ntfsOpenEntry(dir->vd, path); if (!dir->ni) { ntfsUnlock(dir->vd); r->_errno = ENOENT; return NULL; } // Ensure that this directory is indeed a directory if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(dir->vd, dir->ni); ntfsUnlock(dir->vd); r->_errno = ENOTDIR; return NULL; } // Read the directory dir->first = dir->current = NULL; if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) { ntfsCloseDir(dir); ntfsUnlock(dir->vd); r->_errno = errno; return NULL; } // Move to the first entry in the directory dir->current = dir->first; // Update directory times ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Insert the directory into the double-linked FILO list of open directories if (dir->vd->firstOpenDir) { dir->nextOpenDir = dir->vd->firstOpenDir; dir->vd->firstOpenDir->prevOpenDir = dir; } else { dir->nextOpenDir = NULL; } dir->prevOpenDir = NULL; dir->vd->cwd_ni = dir->ni; dir->vd->firstOpenDir = dir; dir->vd->openDirCount++; // Unlock ntfsUnlock(dir->vd); return dirState; }
int ntfsUnlink (ntfs_vd *vd, const char *path) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; char *name = NULL; ntfschar *uname = NULL; int uname_len; int res = 0; // Sanity check if (!vd) { errno = ENODEV; return -1; } // Get the actual path of the entry path = ntfsRealPath(path); if (!path) { errno = EINVAL; return -1; } // Lock ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible dir = strdup(path); if (!dir) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if (name) name++; else name = dir; uname_len = ntfsLocalToUnicode(name, &uname); if (uname_len < 0) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if(name) { name++; name[0] = 0; } // Find the entry ni = ntfsOpenEntry(vd, path); if (!ni) { errno = ENOENT; res = -1; goto cleanup; } // Open the entries parent directory dir_ni = ntfsOpenEntry(vd, dir); if (!dir_ni) { errno = ENOENT; res = -1; goto cleanup; } // Unlink the entry from its parent if (ntfs_delete(vd->vol, path, ni, dir_ni, uname, uname_len)) { res = -1; } // Force the underlying device to sync ntfs_device_sync(vd->dev); // ntfs_delete() ALWAYS closes ni and dir_ni; so no need for us to anymore dir_ni = ni = NULL; cleanup: if(dir_ni) ntfsCloseEntry(vd, dir_ni); if(ni) ntfsCloseEntry(vd, ni); // use free because the value was not allocated with ntfs_alloc if(uname) free(uname); if(dir) ntfs_free(dir); // Unlock ntfsUnlock(vd); return 0; }
int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; char *name = NULL; ntfschar *uname = NULL; int uname_len; int res = 0; // Sanity check if (!vd) { errno = ENODEV; return -1; } // You cannot link between devices if(vd != ntfsGetVolume(new_path)) { errno = EXDEV; return -1; } // Get the actual paths of the entry old_path = ntfsRealPath(old_path); new_path = ntfsRealPath(new_path); if (!old_path || !new_path) { errno = EINVAL; return -1; } // Lock ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible, clean it up dir = strdup(new_path); if (!dir) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if (name) name++; else name = dir; uname_len = ntfsLocalToUnicode(name, &uname); if (uname_len < 0) { errno = EINVAL; goto cleanup; } *name = 0; // Find the entry ni = ntfsOpenEntry(vd, old_path); if (!ni) { errno = ENOENT; res = -1; goto cleanup; } // Open the entries new parent directory dir_ni = ntfsOpenEntry(vd, dir); if (!dir_ni) { errno = ENOENT; res = -1; goto cleanup; } // Link the entry to its new parent if (ntfs_link(ni, dir_ni, uname, uname_len)) { res = -1; goto cleanup; } // Update entry times ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); // Sync the entry to disc ntfsSync(vd, ni); cleanup: if(dir_ni) ntfsCloseEntry(vd, dir_ni); if(ni) ntfsCloseEntry(vd, ni); // use free because the value was not allocated with ntfs_alloc if(uname) free(uname); if(dir) ntfs_free(dir); // Unlock ntfsUnlock(vd); return res; }
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; char *name = NULL; ntfschar *uname = NULL, *utarget = NULL; int uname_len, utarget_len; // Sanity check if (!vd) { errno = ENODEV; return NULL; } // You cannot link between devices if(target) { if(vd != ntfsGetVolume(target)) { errno = EXDEV; return NULL; } } // Get the actual paths of the entry path = ntfsRealPath(path); target = ntfsRealPath(target); if (!path) { errno = EINVAL; return NULL; } // Lock ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible, clean it up dir = strdup(path); if (!dir) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if (name) name++; else name = dir; uname_len = ntfsLocalToUnicode(name, &uname); if (uname_len < 0) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if(name) { name++; name[0] = 0; } // Open the entries parent directory dir_ni = ntfsOpenEntry(vd, dir); if (!dir_ni) { goto cleanup; } // Create the entry switch (type) { // Symbolic link case S_IFLNK: if (!target) { errno = EINVAL; goto cleanup; } utarget_len = ntfsLocalToUnicode(target, &utarget); if (utarget_len < 0) { errno = EINVAL; goto cleanup; } ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len); break; // Directory or file case S_IFDIR: case S_IFREG: ni = ntfs_create(dir_ni, 0, uname, uname_len, type); break; } // If the entry was created if (ni) { // Mark the entry for archiving ni->flags |= FILE_ATTR_ARCHIVE; // Mark the entry as dirty NInoSetDirty(ni); // Sync the entry to disc ntfsSync(vd, ni); // Update parent directories times ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); } cleanup: if(dir_ni) ntfsCloseEntry(vd, dir_ni); // use free because the value was not allocated with ntfs_alloc if(utarget) free(utarget); if(uname) free(uname); if(dir) ntfs_free(dir); // Unlock ntfsUnlock(vd); return ni; }