int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState) { ntfs_log_trace("dirState %p\n", dirState); ntfs_dir_state* dir = STATE(dirState); // Sanity check if (!dir || !dir->vd || !dir->ni) { r->_errno = EBADF; return -1; } // Lock ntfsLock(dir->vd); // Move to the first entry in the directory dir->current = dir->first; // Update directory times ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Unlock ntfsUnlock(dir->vd); return 0; }
void ntfsCloseFile(ntfs_file_state *file) { // Sanity check if (!file || !file->vd) return; // Special case fix ups for compressed and/or encrypted files if (file->compressed) ntfs_attr_pclose(file->data_na); #ifdef HAVE_SETXATTR if (file->encrypted) ntfs_efs_fixup_attribute(NULL, file->data_na); #endif // Close the file data attribute (if open) if (file->data_na) ntfs_attr_close(file->data_na); // Sync the file (and its attributes) to disc if (file->write) { ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); ntfsSync(file->vd, file->ni); } if (file->read) ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); // Close the file (if open) if (file->ni) ntfsCloseEntry(file->vd, file->ni); // Reset the file state file->ni = NULL; file->data_na = NULL; file->flags = 0; file->read = false; file->write = false; file->append = false; file->pos = 0; file->len = 0; return; }
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_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_ftruncate_r(struct _reent *r, int fd, off_t len) { ntfs_log_trace("fd %p, len %llu\n", (void *) fd, (u64) len); ntfs_file_state* file = STATE(((intptr_t)(s64)fd)); //ntfs_file_state* file = STATE(((s64) fd)); // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Lock ntfsLock(file->vd); // Check that we are allowed to write to this file if (!file->write) { ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // For compressed files, only deleting and expanding contents are implemented if (file->compressed && len > 0 && len < file->data_na->initialized_size) { ntfsUnlock(file->vd); r->_errno = ENOTSUP; return -1; } // Resize the files data attribute, either by expanding or truncating if (file->compressed && len > file->data_na->initialized_size) { char zero = 0; if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } } else { if (ntfs_attr_truncate(file->data_na, len)) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } } // Mark the file for archiving (if we actually changed something) if (file->len != file->data_na->data_size) file->ni->flags |= FILE_ATTR_ARCHIVE; // Update file times (if we actually changed something) if (file->len != file->data_na->data_size) ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); // Update the files data length file->len = file->data_na->data_size; // Sync the file (and its attributes) to disc ntfsSync(file->vd, file->ni); // Unlock ntfsUnlock(file->vd); return 0; }
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; }
ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len); ntfs_file_state* file = STATE(fd); ssize_t written = 0; off_t old_pos = 0; // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Short circuit cases where we don't actually have to do anything if (!ptr || len <= 0) { return 0; } // Lock ntfsLock(file->vd); // Check that we are allowed to write to this file if (!file->write) { ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // If we are in append mode, backup the current position and move to the end of the file if (file->append) { old_pos = file->pos; file->pos = file->len; } // Write to the files data atrribute while (len) { ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); if (ret <= 0) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } len -= ret; file->pos += ret; written += ret; } // If we are in append mode, restore the current position to were it was prior to this write if (file->append) { file->pos = old_pos; } // Mark the file for archiving (if we actually wrote something) if (written) file->ni->flags |= FILE_ATTR_ARCHIVE; // Update file times (if we actually wrote something) if (written) ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME); // Update the files data length file->len = file->data_na->data_size; // Unlock ntfsUnlock(file->vd); return written; }
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; }