const char * gnupg_bindir (void) { #if defined (HAVE_W32CE_SYSTEM) static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "bin", NULL); return name; #elif defined(HAVE_W32_SYSTEM) const char *rdir; rdir = w32_rootdir (); if (w32_bin_is_bin) { static char *name; if (!name) name = xstrconcat (rdir, DIRSEP_S "bin", NULL); return name; } else return rdir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_BINDIR; #endif /*!HAVE_W32_SYSTEM*/ }
/*! \brief Check if working with directory Check whether name is a directory. \param[out] dst \param dir Parent directory name \param[in,out] name Child file/directory name \param stamp Time stamp applied to the parent directory \param ornamelen Length of the child file/directory name without the version suffix \retval ZFS_OK Child is a directory; dst is filled in and name suffix is truncated \retval ENOENT Child in not a directory */ int32_t version_is_directory(string * dst, char *dir, string * name, time_t stamp, time_t * dirstamp, int orgnamelen) { char *x; struct stat st; x = xstrconcat(dir, name->str, NULL); if (orgnamelen) x[strlen(dir) + orgnamelen] = '\0'; if (!lstat(x, &st) && S_ISDIR(st.st_mode)) { // it is a directory dst->str = x; dst->len = strlen(dst->str); free(dir); if (orgnamelen) { if (dirstamp) *dirstamp = stamp; name->str[orgnamelen] = '\0'; name->len = orgnamelen; } RETURN_INT(ZFS_OK); } free(x); RETURN_INT(ENOENT); }
/*! \brief Generate file version specifier Add a version suffix to the specified file path. Suffix is generated from the current time. \param path Complete file path. \param[out] verpath Complete file path including a version suffix. */ int32_t version_generate_filename(char *path, string * verpath) { time_t t; char stamp[VERSION_MAX_SPECIFIER_LENGTH]; // even unsigned long long int // // will fit in here // get current time if (time(&t) == -1) { message(LOG_WARNING, FACILITY_VERSION, "version_generate_filename: time returned error=%d\n", errno); RETURN_INT(errno); } // convert to string sprintf(stamp, "%ld", t); verpath->str = xstrconcat(path, VERSION_NAME_SPECIFIER_S, stamp, NULL); verpath->len = strlen(verpath->str); message(LOG_DEBUG, FACILITY_VERSION, "version_generate_filename: path=%s, stamp=%s\n", path, stamp); RETURN_INT(ZFS_OK); }
/* Return the default pinentry name. If RESET is true the internal cache is first flushed. */ static const char * get_default_pinentry_name (int reset) { static struct { const char *(*rfnc)(void); const char *name; } names[] = { /* The first entry is what we return in case we found no other pinentry. */ { gnupg_bindir, DIRSEP_S "pinentry" EXEEXT_S }, #ifdef HAVE_W32_SYSTEM /* Try Gpg4win directory (with bin and without.) */ { w32_rootdir, "\\..\\Gpg4win\\bin\\pinentry.exe" }, { w32_rootdir, "\\..\\Gpg4win\\pinentry.exe" }, /* Try old Gpgwin directory. */ { w32_rootdir, "\\..\\GNU\\GnuPG\\pinentry.exe" }, /* Try a Pinentry from the common GNU dir. */ { w32_rootdir, "\\..\\GNU\\bin\\pinentry.exe" }, #endif /* Last chance is a pinentry-basic (which comes with the GnuPG 2.1 Windows installer). */ { gnupg_bindir, DIRSEP_S "pinentry-basic" EXEEXT_S } }; static char *name; if (reset) { xfree (name); name = NULL; } if (!name) { int i; for (i=0; i < DIM(names); i++) { char *name2; name2 = xstrconcat (names[i].rfnc (), names[i].name, NULL); if (!access (name2, F_OK)) { /* Use that pinentry. */ xfree (name); name = name2; break; } if (!i) /* Store the first as fallback return. */ name = name2; else xfree (name2); } } return name; }
/*! \brief Remove all version files from a directory Remove all version files from specified directory. This function is called from rmdir operations to make sure all files are deleted even version files are not displayed. Works correctly only if there are no other files but versions. \param path Complete path to the directory */ int32_t version_rmdir_versions(char *path) { int32_t r; int working = 1; ZFS_DIR * dirp = zfs_opendir(path); if (dirp == NULL) RETURN_INT(errno); long dir_start_pos = zfs_telldir(dirp); while (working) { /* Always start from the beginning. We are modifying the contents of the directory. And deleting files, so we will continue until there are no (version) files. */ zfs_seekdir(dirp, dir_start_pos); /* Comments to the work with getdents can be found in other functions. */ zfs_dirent entry, *de; r = zfs_readdir_r(dirp, &entry, &de); if (r > 0) { zfs_closedir(dirp); RETURN_INT(r); } else if (r == 0 && de == NULL) // end of list { break; } /* If we delete at least one file, we should start over again. */ working = 0; /* Hide version files or convert their names or select them for storage. */ if (strchr(de->d_name, VERSION_NAME_SPECIFIER_C)) { char *f; f = xstrconcat(path, DIRECTORY_SEPARATOR, de->d_name, NULL); unlink(f); free(f); working = 1; } } zfs_closedir(dirp); RETURN_INT(ZFS_OK); }
const char * gnupg_datadir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "gnupg", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_DATADIR; #endif /*!HAVE_W32_SYSTEM*/ }
const char * gnupg_localedir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "locale", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return LOCALEDIR; #endif /*!HAVE_W32_SYSTEM*/ }
/*! \brief Delete version file Delete version file and its respective interval file. \param path Complete path of the file */ int32_t version_unlink_version_file(char *path) { int32_t r; char *x; // unlink both version file and interval file x = xstrconcat(path, VERSION_INTERVAL_FILE_ADD, NULL); unlink(x); free(x); r = unlink(path); RETURN_INT(r); }
/* Get the standard home directory. In general this function should not be used as it does not consider a registry value (under W32) or the GNUPGHOME environment variable. It is better to use default_homedir(). */ const char * standard_homedir (void) { #ifdef HAVE_W32_SYSTEM static const char *dir; if (!dir) { const char *rdir; rdir = w32_rootdir (); if (w32_portable_app) { dir = xstrconcat (rdir, DIRSEP_S "home", NULL); } else { char path[MAX_PATH]; /* It might be better to use LOCAL_APPDATA because this is defined as "non roaming" and thus more likely to be kept locally. For private keys this is desired. However, given that many users copy private keys anyway forth and back, using a system roaming services might be better than to let them do it manually. A security conscious user will anyway use the registry entry to have better control. */ if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, path) >= 0) { char *tmp = xmalloc (strlen (path) + 6 +1); strcpy (stpcpy (tmp, path), "\\gnupg"); dir = tmp; /* Try to create the directory if it does not yet exists. */ if (access (dir, F_OK)) w32_try_mkdir (dir); } else dir = GNUPG_DEFAULT_HOMEDIR; } } return dir; #else/*!HAVE_W32_SYSTEM*/ return GNUPG_DEFAULT_HOMEDIR; #endif /*!HAVE_W32_SYSTEM*/ }
/* Check whether gpgconf is installed and if so read the gpgconf.ctl file. */ static void check_portable_app (const char *dir) { char *fname; fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); if (access (fname, F_OK)) log_error ("required binary '%s' is not installed\n", fname); else { strcpy (fname + strlen (fname) - 3, "ctl"); if (!access (fname, F_OK)) { /* gpgconf.ctl file found. Record this fact. */ w32_portable_app = 1; /* FIXME: We should read the file to detect special flags and print a warning if we don't understand them. */ } } xfree (fname); }
/* Check whether gpgconf is installed and if so read the gpgconf.ctl file. */ static void check_portable_app (const char *dir) { char *fname; fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); if (!access (fname, F_OK)) { strcpy (fname + strlen (fname) - 3, "ctl"); if (!access (fname, F_OK)) { /* gpgconf.ctl file found. Record this fact. */ w32_portable_app = 1; { unsigned int flags; log_get_prefix (&flags); log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); } /* FIXME: We should read the file to detect special flags and print a warning if we don't understand them */ } } xfree (fname); }
/*! \brief Delete version file Delete version file and its respective interval file. \param dir Dentry of the directory the file is in \param vol Volume the file is on \param name Name of the file */ bool version_retent_file(internal_dentry dir, volume vol, char *name) { string path; char *dst; acquire_dentry(dir); zfsd_mutex_lock(&vol->mutex); zfsd_mutex_lock(&fh_mutex); build_local_path(&path, vol, dir); release_dentry(dir); zfsd_mutex_unlock(&vol->mutex); zfsd_mutex_unlock(&fh_mutex); dst = xstrconcat(path.str, DIRECTORY_SEPARATOR, name, NULL); version_unlink_version_file(dst); free(dst); // file is deleted immediately return true; }
static void test_xstrconcat (void) { char *out; out = xstrconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "1", "2", "3", "4", "5", "6", "7", NULL); if (!out) fail (0); #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ out = xstrconcat (NULL); if (!out) fail (1); #endif out = xstrconcat (NULL, NULL); if (!out) fail (1); out = xstrconcat ("", NULL); if (!out || *out) fail (1); xfree (out); out = xstrconcat ("", "", NULL); if (!out || *out) fail (2); xfree (out); out = xstrconcat ("a", "b", NULL); if (!out || strcmp (out, "ab")) fail (3); xfree (out); out = xstrconcat ("a", "b", "c", NULL); if (!out || strcmp (out, "abc")) fail (3); xfree (out); out = xstrconcat ("a", "b", "cc", NULL); if (!out || strcmp (out, "abcc")) fail (4); xfree (out); out = xstrconcat ("a1", "b1", "c1", NULL); if (!out || strcmp (out, "a1b1c1")) fail (4); xfree (out); out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); if (!out || strcmp (out, " long b --even-longer--")) fail (5); xfree (out); out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); if (!out || strcmp (out, " long b --even-longer--")) fail (5); xfree (out); }
/*! \brief Create interval file name Create interval file name. \param[out] path Complete path of the interval file \param fh Internal file handle */ static void version_build_interval_path(string * path, internal_fh fh) { path->str = xstrconcat(fh->version_path, VERSION_INTERVAL_FILE_ADD, NULL); path->len = strlen(path->str); }
/*! \brief Find version files for specified time stamp. Find version files that contain data for the specified time stamp, i.e. all newer version files that combined together cover the whole file. \param path Complete path of the directory \param name Name of the file \param stamp Time stamp of the moment in time \param[out] ino Inode of the first newer version file \param[out] v varray filled with list of versions \see version_item_def */ static int32_t version_browse_dir(char *path, char *name, time_t * stamp, uint32_t * ino, varray * v) { int32_t r; unsigned int nl; time_t res = 0; long current_ino = 0; nl = strlen(name); ZFS_DIR * dirp = zfs_opendir(path); if (dirp == NULL) RETURN_INT(errno); while (1) { zfs_dirent entry, *de; r = zfs_readdir_r(dirp, &entry, &de); if (r > 0) // zfs_readdir_r has failed { zfs_closedir(dirp); RETURN_INT(r); } else if (r == 0 && de == NULL) // end of list { if (ino) { if (res) *stamp = res; else { // check modtime if version existed in that time struct stat st; char *x; time_t mtime = 0; x = xstrconcat(path, DIRECTORY_SEPARATOR, name, NULL); if (!stat(x, &st)) mtime = st.st_mtime; free(x); if (mtime && (mtime <= *stamp)) *ino = current_ino; else *ino = 0; *stamp = 0; } } zfs_closedir(dirp); RETURN_INT(ZFS_OK); } // check if we have our file if (!strncmp(de->d_name, name, nl)) { // check if it is current file if (strlen(de->d_name) == nl) { current_ino = de->d_ino; if (v) { version_item item; item.stamp = INT32_MAX; item.name = xstrdup(de->d_name); item.intervals = NULL; item.path = NULL; VARRAY_PUSH(*v, item, version_item); } continue; } // get name stamp char *p, *q; time_t t; p = strchr(de->d_name, VERSION_NAME_SPECIFIER_C); if (!p) continue; *p = '\0'; p++; q = strchr(p, '.'); if (q) { *q = '\0'; q++; // skip interval files if (*q == 'i') continue; } // get version file stamp t = atoi(p); // compare timestamps if we are looking for a file if (ino && (t >= *stamp) && (!res || (t < res))) { // check modtime if version existed in that time // in fact, this should be valid only for one file struct stat st; char *x; time_t mtime = 0; x = xstrconcat(path, DIRECTORY_SEPARATOR, de->d_name, VERSION_NAME_SPECIFIER_S, p, NULL); if (!stat(x, &st)) mtime = st.st_mtime; free(x); if (mtime && (mtime <= *stamp)) { res = t; *ino = de->d_ino; } } // add into varray if we want list of version files if (v && (t > *stamp)) { version_item item; *(--p) = VERSION_NAME_SPECIFIER_C; item.stamp = t; item.name = xstrdup(de->d_name); item.intervals = NULL; item.path = NULL; VARRAY_PUSH(*v, item, version_item); } } } }
/**************** * Copy the option file skeleton for NAME to the given directory. * Returns true if the new option file has any option. */ static int copy_options_file (const char *destdir, const char *name) { const char *datadir = gnupg_datadir (); char *fname; FILE *src, *dst; int linefeeds=0; int c; mode_t oldmask; int esc = 0; int any_option = 0; if (opt.dry_run) return 0; fname = xstrconcat (datadir, DIRSEP_S, name, "-conf", SKELEXT, NULL); src = fopen (fname, "r"); if (src && is_secured_file (fileno (src))) { fclose (src); src = NULL; gpg_err_set_errno (EPERM); } if (!src) { log_info (_("can't open '%s': %s\n"), fname, strerror(errno)); xfree(fname); return 0; } xfree (fname); fname = xstrconcat (destdir, DIRSEP_S, name, EXTSEP_S, "conf", NULL); oldmask = umask (077); if (is_secured_filename (fname)) { dst = NULL; gpg_err_set_errno (EPERM); } else dst = fopen( fname, "w" ); umask (oldmask); if (!dst) { log_info (_("can't create '%s': %s\n"), fname, strerror(errno) ); fclose (src); xfree (fname); return 0; } while ((c = getc (src)) != EOF) { if (linefeeds < 3) { if (c == '\n') linefeeds++; } else { putc (c, dst); if (c== '\n') esc = 1; else if (esc == 1) { if (c == ' ' || c == '\t') ; else if (c == '#') esc = 2; else any_option = 1; } } } fclose (dst); fclose (src); log_info (_("new configuration file '%s' created\n"), fname); xfree (fname); return any_option; }
/* * Make an output filename for the inputfile INAME. * Returns an IOBUF and an errorcode * Mode 0 = use ".gpg" * 1 = use ".asc" * 2 = use ".sig" * 3 = use ".rev" * * If INP_FD is not -1 the function simply creates an IOBUF for that * file descriptor and ignore INAME and MODE. Note that INP_FD won't * be closed if the returned IOBUF is closed. With RESTRICTEDPERM a * file will be created with mode 700 if possible. */ int open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a) { int rc = 0; *a = NULL; if (inp_fd != -1) { char xname[64]; *a = iobuf_fdopen_nc (inp_fd, "wb"); if (!*a) { rc = gpg_error_from_syserror (); snprintf (xname, sizeof xname, "[fd %d]", inp_fd); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc)); } else if (opt.verbose) { snprintf (xname, sizeof xname, "[fd %d]", inp_fd); log_info (_("writing to '%s'\n"), xname); } } else if (iobuf_is_pipe_filename (iname) && !opt.outfile) { *a = iobuf_create (NULL, 0); if ( !*a ) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), "[stdout]", strerror(errno) ); } else if ( opt.verbose ) log_info (_("writing to stdout\n")); } else { char *buf = NULL; const char *name; if (opt.dry_run) name = NAME_OF_DEV_NULL; else if (opt.outfile) name = opt.outfile; else { #ifdef USE_ONLY_8DOT3 if (opt.mangle_dos_filenames) { /* It is quite common for DOS systems to have only one dot in a filename. If we have something like this, we simple replace the suffix except in cases where the suffix is larger than 3 characters and not the same as the new one. We don't map the filenames to 8.3 because this is a duty of the file system. */ char *dot; const char *newsfx; newsfx = (mode==1 ? ".asc" : mode==2 ? ".sig" : mode==3 ? ".rev" : ".gpg"); buf = xmalloc (strlen(iname)+4+1); strcpy (buf, iname); dot = strchr (buf, '.' ); if ( dot && dot > buf && dot[1] && strlen(dot) <= 4 && CMP_FILENAME (newsfx, dot) ) strcpy (dot, newsfx); else if (dot && !dot[1]) /* Do not duplicate a dot. */ strcpy (dot, newsfx+1); else strcat (buf, newsfx); } if (!buf) #endif /* USE_ONLY_8DOT3 */ { buf = xstrconcat (iname, (mode==1 ? EXTSEP_S "asc" : mode==2 ? EXTSEP_S "sig" : mode==3 ? EXTSEP_S "rev" : /* */ EXTSEP_S GPGEXT_GPG), NULL); } name = buf; } rc = 0; while ( !overwrite_filep (name) ) { char *tmp = ask_outfile_name (NULL, 0); if ( !tmp || !*tmp ) { xfree (tmp); rc = gpg_error (GPG_ERR_EEXIST); break; } xfree (buf); name = buf = tmp; } if ( !rc ) { if (is_secured_filename (name) ) { *a = NULL; gpg_err_set_errno (EPERM); } else *a = iobuf_create (name, restrictedperm); if (!*a) { rc = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), name, strerror(errno) ); } else if( opt.verbose ) log_info (_("writing to '%s'\n"), name ); } xfree(buf); } if (*a) iobuf_ioctl (*a, IOBUF_IOCTL_NO_CACHE, 1, NULL); return rc; }
/*! \brief Find first newer version Find oldest version of the file that is newer than the specified time stamp. \param dir Complete path of the directory \param[in,out] name File name; contains version suffix on return \param stamp Time stamp */ int32_t version_find_version(char *dir, string * name, time_t stamp) { char *sname; char *p; uint32_t ino = 0; struct stat st; char *x; char ver[VERSION_MAX_SPECIFIER_LENGTH]; sname = xstrdup(name->str); p = strchr(sname, VERSION_NAME_SPECIFIER_C); if (p) *p = '\0'; // check for exact version file snprintf(ver, sizeof(ver), "%ld", stamp); x = xstrconcat(dir, DIRECTORY_SEPARATOR, sname, VERSION_NAME_SPECIFIER_S, ver, NULL); if (!stat(x, &st)) { free(x); // name is allocated in kernel call via FUSE // free (name->str); name->str = xstrconcat(sname, VERSION_NAME_SPECIFIER_S, ver, NULL);; name->len = strlen(name->str); free(sname); RETURN_INT(ZFS_OK); } free(x); // look for first newer version_browse_dir(dir, sname, &stamp, &ino, NULL); message(LOG_DEBUG, FACILITY_VERSION, "Using stamp=%d, sname=%s, ino=%u\n", stamp, sname, ino); if (stamp > 0) { // found version file free(name->str); snprintf(ver, sizeof(ver), "%ld", stamp); name->str = xstrconcat(sname, VERSION_NAME_SPECIFIER_S, ver, NULL); name->len = strlen(name->str); } else if (ino > 0) { // found current file p = strchr(name->str, VERSION_NAME_SPECIFIER_C); if (p) *p = '\0'; name->len = strlen(name->str); } else { free(sname); RETURN_INT(ENOENT); } free(sname); RETURN_INT(ZFS_OK); }
/* Return the name of the cache directory. The name is allocated in a static area on the first use. Windows only: If the directory does not exist it is created. */ const char * gnupg_cachedir (void) { #ifdef HAVE_W32_SYSTEM static const char *dir; if (!dir) { const char *rdir; rdir = w32_rootdir (); if (w32_portable_app) { dir = xstrconcat (rdir, DIRSEP_S, "var", DIRSEP_S, "cache", DIRSEP_S, "gnupg", NULL); } else { char path[MAX_PATH]; const char *s1[] = { "GNU", "cache", "gnupg", NULL }; int s1_len; const char **comp; s1_len = 0; for (comp = s1; *comp; comp++) s1_len += 1 + strlen (*comp); if (w32_shgetfolderpath (NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, path) >= 0) { char *tmp = xmalloc (strlen (path) + s1_len + 1); char *p; p = stpcpy (tmp, path); for (comp = s1; *comp; comp++) { p = stpcpy (p, "\\"); p = stpcpy (p, *comp); if (access (tmp, F_OK)) w32_try_mkdir (tmp); } dir = tmp; } else { dir = "c:\\temp\\cache\\gnupg"; #ifdef HAVE_W32CE_SYSTEM dir += 2; w32_try_mkdir ("\\temp\\cache"); w32_try_mkdir ("\\temp\\cache\\gnupg"); #endif } } } return dir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME; #endif /*!HAVE_W32_SYSTEM*/ }
/*! \brief Create list of intervals for a file version Create list of intervals together with version files these intervals are stored in that create the whole content of a file version. List is created from interval files during file open. Result is stored in the internal file handle. \param dentry Dentry of the file \param vol Volume the file in on */ int32_t version_build_intervals(internal_dentry dentry, volume vol) { time_t stamp; varray v; char *sname; char *p; version_item *list; unsigned int n, m; unsigned int i, j; string dpath; bool r; r = version_load_interval_tree(dentry->fh); if (!r) RETURN_INT(ZFS_OK); // parse version file name sname = xstrdup(dentry->name.str); p = strchr(sname, VERSION_NAME_SPECIFIER_C); if (!p) { free(sname); RETURN_INT(ENOENT); } *p = '\0'; stamp = atoi(p + 1); // get list of version files that newer than timestamp zfsd_mutex_lock(&dentry->parent->fh->mutex); build_local_path(&dpath, vol, dentry->parent); zfsd_mutex_unlock(&dentry->parent->fh->mutex); varray_create(&v, sizeof(version_item), 1); version_browse_dir(dpath.str, sname, &stamp, NULL, &v); free(sname); n = VARRAY_USED(v); list = (version_item *) xmalloc(n * sizeof(version_item)); for (i = 0; i < n; i++) { list[i] = VARRAY_ACCESS(v, i, version_item); list[i].path = xstrconcat(dpath.str, DIRECTORY_SEPARATOR, list[i].name, NULL); } varray_destroy(&v); free(dpath.str); // sort the list qsort(list, n, sizeof(version_item), comp_version_items); // get intervals for version files in the list for (i = 0; i < n; i++) { // read int fd; char *ival; if (!list[i].path) continue; ival = xstrconcat(list[i].path, VERSION_INTERVAL_FILE_ADD, NULL); fd = open(ival, O_RDONLY); free(ival); if (fd > 0) { // read interval file struct stat st; list[i].intervals = interval_tree_create(1, NULL); if (fstat(fd, &st) < 0) { CLEAR_VERSION_ITEM(list[i]); } else if (!interval_tree_read (list[i].intervals, fd, st.st_size / sizeof(interval))) CLEAR_VERSION_ITEM(list[i]); close(fd); } else if (errno == ENOENT) { // complete file i++; break; } // invalid version file else CLEAR_VERSION_ITEM(list[i]); } // delete all redundant intervals m = i; for (j = i + 1; j < n; j++) CLEAR_VERSION_ITEM(list[j]); dentry->fh->version_list = list; dentry->fh->version_list_length = m; RETURN_INT(ZFS_OK); }