/* * Retrieve metadata in a given root for a given file. It does not resolve * paths yet, but gets the preferred versions. */ metadata_t *parse_metadata_for_file(char *root, char *filename) { char *metafile, *dflfile, *metapath, *dflpath; metadata_t *metadata; /* Build the path for the metadata file */ metafile = helper_get_file_name(filename, "metadata"); metapath = helper_build_composite("SS", "/", root, metafile); free(metafile); /* Read the partial metadata */ metadata = parse_metadata_file(metapath); if (!metadata) { /* No metadata, we don't know about this file yet */ free(metapath); return NULL; } /* Build the path for the default version file */ dflfile = helper_get_file_name(filename, "dfl-meta"); dflpath = helper_build_composite("SS", "/", root, dflfile); free(dflfile); /* Parse the default version file */ parse_default_file(dflpath, &metadata->md_dfl_vid, &metadata->md_dfl_svid); free(metapath); free(dflpath); return metadata; }
int ea_getxattr_rcs(const char *path, const char *name, char *value, size_t size) { metadata_t *metadata; version_t *version; rcs_ignore_deleted = 1; metadata = rcs_translate_to_metadata(path, rcs_version_path); rcs_ignore_deleted = 0; if (!metadata) return -ENOENT; if (!strcmp(name, "rcs.purge")) { /* This one is write-only */ return -EPERM; } else if (!strcmp(name, "rcs.locked_version")) { char buffer[64]; int vid, svid; if (metadata->md_deleted) { strcpy(buffer, "0.0"); } else { if (metadata->md_dfl_vid == -1) { vid = metadata->md_versions->v_vid; svid = metadata->md_versions->v_svid; } else { vid = metadata->md_dfl_vid; svid = metadata->md_dfl_svid; } /* Get the version number */ snprintf(buffer, 64, "%i.%i", vid, svid); } /* Handle the EA protocol */ if (size == 0) return strlen(buffer); if (strlen(buffer) > size) return -ERANGE; strcpy(value, buffer); return strlen(buffer); } else if (!strcmp(name, "rcs.metadata_dump")) { char **array, *result; unsigned int count; int res; /* * We need to pass the version metadata to userspace, but we also need * to pass the file type and modification type from the stat syscall, * since the userspace program may be running as a non-root, and thus * can't see the version store. */ for (count = 0, version = metadata->md_versions; version; version = version->v_next) count++; array = safe_malloc(sizeof(char *) * (count + 1)); memset(array, 0, sizeof(char *) * (count + 1)); /* Traverse the version list and build the individual strings */ for (count = 0, version = metadata->md_versions; version; version = version->v_next) { struct stat st_data; /* stat() the real file, but just ignore failures (bad version ?) */ if (lstat(version->v_rfile, &st_data) < 0) { st_data.st_mode = S_IFREG; st_data.st_mtime = -1; } else st_data.st_mode &= ~07777; if (asprintf(&array[count], "%d:%d:%d:%d:%d:%lld:%ld", version->v_vid, version->v_svid, version->v_mode | st_data.st_mode, version->v_uid, version->v_gid, (long long)st_data.st_size, st_data.st_mtime) < 0) { unsigned int i; /* Free everything if it failed */ for (i = 0; i < count; i++) free(array[i]); free(array); return -ENOMEM; } count++; } /* Build the final string */ result = helper_build_composite("A", "|", array); helper_free_array(array); /* Handle the EA protocol */ if (size == 0) res = strlen(result); else if (strlen(result) > size) res = -ERANGE; else { strcpy(value, result); res = strlen(result); } free(result); return res; } else if (!strcmp(name, "rcs.list_dir")) { struct dirent *entry; int res; DIR *dir; char *result; size_t result_size; string_list_t *file_list; string_list_t **file_list_ptr; file_list = NULL; file_list_ptr = &file_list; rcs_ignore_deleted = 1; version = rcs_find_version(metadata, LATEST, LATEST); rcs_ignore_deleted = 0; dir = opendir(version->v_rfile); if (!dir) { return -errno; } /* Find the metadata files */ while ((entry = readdir(dir))) { /* * We want the metadata files (because a versioned file is * behind them) */ if (!strcmp(entry->d_name, METADATA_PREFIX)) { /* This is the root's metadata, ignore it */ continue; } else if (!strncmp(entry->d_name, METADATA_PREFIX, strlen(METADATA_PREFIX))) { char *file; metadata_t *file_metadata; int len; if (strcmp(path, "/")) file = helper_build_composite("SS", "/", path, entry->d_name + strlen(METADATA_PREFIX)); else file = helper_build_composite("-S", "/", entry->d_name + strlen(METADATA_PREFIX)); rcs_ignore_deleted = 1; file_metadata = rcs_translate_to_metadata(file, rcs_version_path); rcs_ignore_deleted = 0; free(file); if (!file_metadata) { closedir(dir); return -ENOENT; } *file_list_ptr = safe_malloc(sizeof(string_list_t)); memset(*file_list_ptr, 0, sizeof(string_list_t)); if (file_metadata->md_deleted) len = asprintf(&(*file_list_ptr)->sl_data, "0:0:0:0:0:0:0:%s", entry->d_name + strlen(METADATA_PREFIX)); else { version_t *file_version; struct stat st_data; file_version = rcs_find_version(file_metadata, LATEST, LATEST); if (!file_version) { helper_free_string_list(file_list); closedir(dir); return -ENOENT; } /* stat() the real file, but just ignore failures (bad version ?) */ if (lstat(file_version->v_rfile, &st_data) < 0) { st_data.st_mode = S_IFREG; st_data.st_mtime = -1; } else st_data.st_mode &= ~07777; len = asprintf(&(*file_list_ptr)->sl_data, "%d:%d:%d:%d:%d:%lld:%ld:%s", file_version->v_vid, file_version->v_svid, file_version->v_mode | st_data.st_mode, file_version->v_uid, file_version->v_gid, (long long)st_data.st_size, st_data.st_mtime, entry->d_name + strlen(METADATA_PREFIX)); } if (len < 0) { /* Free everything if it failed */ helper_free_string_list(file_list); closedir(dir); return -ENOMEM; } file_list_ptr = &(*file_list_ptr)->sl_next; } } closedir(dir); /* Build the final string */ result_size = helper_compose_string_list(&result, file_list, '\0'); helper_free_string_list(file_list); /* Handle the EA protocol */ if (size == 0) res = result_size; else if (result_size > size) res = -ERANGE; else { memcpy(value, result, result_size); res = result_size; } free(result); return res; } else { /* unknown rcs.*-attribute */ return -EPERM; } }
static int callback_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t fill) { struct dirent *entry; char *rpath; int res; DIR *dir; rpath = rcs_translate_path(path, rcs_version_path); dir = opendir(rpath); if (!dir) { free(rpath); return -errno; } /* Find the metadata files */ do { entry = readdir(dir); if (entry) { /* * We want the metadata files (because a versionned file is * behind them, the '.' and '..' directories, so that the listing * looks reasonable. */ if (!strcmp(entry->d_name, METADATA_PREFIX)) { /* This is the root's metadata, ignore it */ continue; } else if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { res = fill(h, entry->d_name, 0); if (res != 0) break; } else if (!strncmp(entry->d_name, METADATA_PREFIX, strlen(METADATA_PREFIX))) { metadata_t *metadata; char *file; /* Check if the file is not currently in deleted state */ if (strcmp(path, "/")) file = helper_build_composite("SS", "/", path, entry->d_name + strlen(METADATA_PREFIX)); else file = helper_build_composite("-S", "/", entry->d_name + strlen(METADATA_PREFIX)); metadata = rcs_translate_to_metadata(file, rcs_version_path); free(file); if (metadata && !metadata->md_deleted) { res = fill(h, entry->d_name + strlen(METADATA_PREFIX), 0); if (res != 0) break; } } } } while (entry); closedir(dir); free(rpath); return 0; }
static int callback_rmdir(const char *path) { metadata_t *dir_metadata; version_t *version; struct stat st_rfile; char *metafile; DIR *dir; struct dirent *entry; char *rpath; dir_metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!dir_metadata || dir_metadata->md_deleted) return -ENOENT; version = rcs_find_version(dir_metadata, LATEST, LATEST); if (lstat(version->v_rfile, &st_rfile) == -1) return -errno; if (!S_ISDIR(st_rfile.st_mode)) return -ENOTDIR; rpath = rcs_translate_path(path, rcs_version_path); dir = opendir(rpath); if (!dir) { free(rpath); return -errno; } /* Check if there is any file in the directory */ do { entry = readdir(dir); if (entry) { /* * We want the metadata files (because a versionned file is * behind them, the '.' and '..' directories, so that the listing * looks reasonable. */ if (!strcmp(entry->d_name, METADATA_PREFIX)) { /* This is the root's metadata, ignore it */ continue; } else if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { continue ; } else if (!strncmp(entry->d_name, METADATA_PREFIX, strlen(METADATA_PREFIX))) { metadata_t *metadata; char *file; /* Check if the file is not currently in deleted state */ if (strcmp(path, "/")) file = helper_build_composite("SS", "/", path, entry->d_name + strlen(METADATA_PREFIX)); else file = helper_build_composite("-S", "/", entry->d_name + strlen(METADATA_PREFIX)); metadata = rcs_translate_to_metadata(file, rcs_version_path); free(file); if (metadata && !metadata->md_deleted) { /* we found a file */ closedir(dir); free(rpath); return -ENOTEMPTY; } } } } while (entry); closedir(dir); free(rpath); dir_metadata->md_deleted = 1; metafile = helper_build_meta_name(dir_metadata->md_vfile, METADATA_PREFIX); if (write_metadata_file(metafile, dir_metadata) == -1) { free(metafile); return -errno; } free(metafile); return 0; }