int stat(const char *file_name, struct stat *buf) { return fstatat(AT_FDCWD, file_name, buf, 0); }
/* Recursively scan the directory identified by ST. */ struct directory * scan_directory (struct tar_stat_info *st) { char const *dir = st->orig_file_name; char *dirp = get_directory_entries (st); dev_t device = st->stat.st_dev; bool cmdline = ! st->parent; namebuf_t nbuf; char *tmp; struct directory *directory; char ch; if (! dirp) savedir_error (dir); info_attach_exclist (st); tmp = xstrdup (dir); zap_slashes (tmp); directory = procdir (tmp, st, (cmdline ? PD_FORCE_INIT : 0), &ch); free (tmp); nbuf = namebuf_create (dir); if (dirp) { if (directory->children != NO_CHILDREN) { char *entry; /* directory entry being scanned */ struct dumpdir_iter *itr; makedumpdir (directory, dirp); for (entry = dumpdir_first (directory->dump, 1, &itr); entry; entry = dumpdir_next (itr)) { char *full_name = namebuf_name (nbuf, entry + 1); if (*entry == 'I') /* Ignored entry */ *entry = 'N'; else if (excluded_name (full_name, st)) *entry = 'N'; else { int fd = st->fd; void (*diag) (char const *) = 0; struct tar_stat_info stsub; tar_stat_init (&stsub); if (fd < 0) { errno = - fd; diag = open_diag; } else if (fstatat (fd, entry + 1, &stsub.stat, fstatat_flags) != 0) diag = stat_diag; else if (S_ISDIR (stsub.stat.st_mode)) { int subfd = subfile_open (st, entry + 1, open_read_flags); if (subfd < 0) diag = open_diag; else { stsub.fd = subfd; if (fstat (subfd, &stsub.stat) != 0) diag = stat_diag; } } if (diag) { file_removed_diag (full_name, false, diag); *entry = 'N'; } else if (S_ISDIR (stsub.stat.st_mode)) { int pd_flag = 0; if (!recursion_option) pd_flag |= PD_FORCE_CHILDREN | NO_CHILDREN; else if (directory->children == ALL_CHILDREN) pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN; *entry = 'D'; stsub.parent = st; procdir (full_name, &stsub, pd_flag, entry); restore_parent_fd (&stsub); } else if (one_file_system_option && device != stsub.stat.st_dev) *entry = 'N'; else if (*entry == 'Y') /* New entry, skip further checks */; /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */ else if (OLDER_STAT_TIME (stsub.stat, m) && (!after_date_option || OLDER_STAT_TIME (stsub.stat, c))) *entry = 'N'; else *entry = 'Y'; tar_stat_destroy (&stsub); } } free (itr); } else if (directory->tagfile) maketagdumpdir (directory); } namebuf_free (nbuf); free (dirp); return directory; }
int server_file_index(struct httpd *env, struct client *clt, struct stat *st) { char path[PATH_MAX]; char tmstr[21]; struct http_descriptor *desc = clt->clt_descreq; struct server_config *srv_conf = clt->clt_srv_conf; struct dirent **namelist, *dp; int namesize, i, ret, fd = -1, namewidth, skip; int code = 500; struct evbuffer *evb = NULL; struct media_type *media; const char *stripped, *style; char *escapeduri, *escapedhtml, *escapedpath; struct tm tm; time_t t, dir_mtime; if ((ret = server_file_method(clt)) != 0) { code = ret; goto abort; } /* Request path is already canonicalized */ stripped = server_root_strip(desc->http_path, srv_conf->strip); if ((size_t)snprintf(path, sizeof(path), "%s%s", srv_conf->root, stripped) >= sizeof(path)) goto abort; /* Now open the file, should be readable or we have another problem */ if ((fd = open(path, O_RDONLY)) == -1) goto abort; /* Save last modification time */ dir_mtime = MINIMUM(time(NULL), st->st_mtim.tv_sec); if ((evb = evbuffer_new()) == NULL) goto abort; if ((namesize = scandir(path, &namelist, NULL, alphasort)) == -1) goto abort; /* Indicate failure but continue going through the list */ skip = 0; if ((escapedpath = escape_html(desc->http_path)) == NULL) goto fail; /* A CSS stylesheet allows minimal customization by the user */ style = "body { background-color: white; color: black; font-family: " "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n"; /* Generate simple HTML index document */ if (evbuffer_add_printf(evb, "<!DOCTYPE html>\n" "<html>\n" "<head>\n" "<title>Index of %s</title>\n" "<style type=\"text/css\"><!--\n%s\n--></style>\n" "</head>\n" "<body>\n" "<h1>Index of %s</h1>\n" "<hr>\n<pre>\n", escapedpath, style, escapedpath) == -1) skip = 1; free(escapedpath); for (i = 0; i < namesize; i++) { dp = namelist[i]; if (skip || fstatat(fd, dp->d_name, st, 0) == -1) { free(dp); continue; } t = st->st_mtime; localtime_r(&t, &tm); strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm); namewidth = 51 - strlen(dp->d_name); if ((escapeduri = url_encode(dp->d_name)) == NULL) goto fail; if ((escapedhtml = escape_html(dp->d_name)) == NULL) goto fail; if (dp->d_name[0] == '.' && !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) { /* ignore hidden files starting with a dot */ } else if (S_ISDIR(st->st_mode)) { namewidth -= 1; /* trailing slash */ if (evbuffer_add_printf(evb, "<a href=\"%s%s/\">%s/</a>%*s%s%20s\n", strchr(escapeduri, ':') != NULL ? "./" : "", escapeduri, escapedhtml, MAXIMUM(namewidth, 0), " ", tmstr, "-") == -1) skip = 1; } else if (S_ISREG(st->st_mode)) { if (evbuffer_add_printf(evb, "<a href=\"%s%s\">%s</a>%*s%s%20llu\n", strchr(escapeduri, ':') != NULL ? "./" : "", escapeduri, escapedhtml, MAXIMUM(namewidth, 0), " ", tmstr, st->st_size) == -1) skip = 1; } free(escapeduri); free(escapedhtml); free(dp); } free(namelist); if (skip || evbuffer_add_printf(evb, "</pre>\n<hr>\n</body>\n</html>\n") == -1) goto abort; close(fd); fd = -1; media = media_find_config(env, srv_conf, "index.html"); ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb), dir_mtime); switch (ret) { case -1: goto fail; case 0: /* Connection is already finished */ evbuffer_free(evb); goto done; default: break; } if (server_bufferevent_write_buffer(clt, evb) == -1) goto fail; evbuffer_free(evb); evb = NULL; bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); if (clt->clt_persist) clt->clt_toread = TOREAD_HTTP_HEADER; else clt->clt_toread = TOREAD_HTTP_NONE; clt->clt_done = 0; done: server_reset_http(clt); return (0); fail: bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE); bufferevent_free(clt->clt_bev); clt->clt_bev = NULL; abort: if (fd != -1) close(fd); if (evb != NULL) evbuffer_free(evb); server_abort_http(clt, code, desc->http_path); return (-1); }
// callback to be fed int vdev_load_all_at. // builds up the children listing of vdevfs_scandirat_context.parent_dir. // if we find a directory, open it and enqueue its file descriptor to dir_queue. // return 0 on success // return -ENOMEM on OOM static int vdevfs_scandirat_context_callback (int dirfd, struct dirent *dent, void *cls) { struct vdevfs_scandirat_context *ctx = (struct vdevfs_scandirat_context *) cls; int rc = 0; int fd = 0; struct stat sb; struct fskit_entry *child; char linkbuf[8193]; // for resolving an underlying symlink char const *method_name; // for logging char *joined_path = NULL; struct vdevfs_scandirat_queue *next = NULL; // skip . and .. if (strcmp (dent->d_name, ".") == 0 || strcmp (dent->d_name, "..") == 0) { return 0; } // learn more... rc = fstatat (dirfd, dent->d_name, &sb, AT_SYMLINK_NOFOLLOW); if (rc != 0) { rc = -errno; // mask errors; just log the serious ones if (rc != -ENOENT && rc != -EACCES) { vdev_error ("fstatat(%d, '%s') rc = %d\n", dirfd, dent->d_name, rc); } return 0; } // directory? get an fd to it if so, so we can keep scanning if (S_ISDIR (sb.st_mode)) { // try to get at it fd = openat (dirfd, dent->d_name, O_RDONLY); if (fd < 0) { rc = -errno; // mask errors; just log the serious ones if (rc != -ENOENT && rc != -EACCES) { vdev_error ("openat(%d, '%s') rc = %d\n", dirfd, dent->d_name, rc); } return 0; } // woo! save it joined_path = vdev_fullpath (ctx->parent_path, dent->d_name, NULL); if (joined_path == NULL) { close (fd); return -ENOMEM; } next = VDEV_CALLOC (struct vdevfs_scandirat_queue, 1); if (next == NULL) { close (fd); free (joined_path); return -ENOMEM; } next->fd = fd; next->path = joined_path; next->next = NULL; ctx->tail->next = next; ctx->tail = ctx->tail->next; }
static gboolean checkout_one_file_at (OstreeRepo *repo, OstreeRepoCheckoutOptions *options, GFile *source, GFileInfo *source_info, int destination_dfd, const char *destination_name, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *checksum; gboolean is_symlink; gboolean can_cache; gboolean need_copy = TRUE; char loose_path_buf[_OSTREE_LOOSE_PATH_MAX]; g_autoptr(GInputStream) input = NULL; g_autoptr(GVariant) xattrs = NULL; gboolean is_whiteout; is_symlink = g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK; checksum = ostree_repo_file_get_checksum ((OstreeRepoFile*)source); is_whiteout = !is_symlink && options->process_whiteouts && g_str_has_prefix (destination_name, WHITEOUT_PREFIX); /* First, see if it's a Docker whiteout, * https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go */ if (is_whiteout) { const char *name = destination_name + (sizeof (WHITEOUT_PREFIX) - 1); if (!name[0]) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid empty whiteout '%s'", name); goto out; } g_assert (name[0] != '/'); /* Sanity */ if (!glnx_shutil_rm_rf_at (destination_dfd, name, cancellable, error)) goto out; need_copy = FALSE; } else if (!is_symlink) { gboolean did_hardlink = FALSE; /* Try to do a hardlink first, if it's a regular file. This also * traverses all parent repos. */ OstreeRepo *current_repo = repo; while (current_repo) { gboolean is_bare = ((current_repo->mode == OSTREE_REPO_MODE_BARE && options->mode == OSTREE_REPO_CHECKOUT_MODE_NONE) || (current_repo->mode == OSTREE_REPO_MODE_BARE_USER && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)); gboolean current_can_cache = (options->enable_uncompressed_cache && current_repo->enable_uncompressed_cache); gboolean is_archive_z2_with_cache = (current_repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER && current_can_cache); /* But only under these conditions */ if (is_bare || is_archive_z2_with_cache) { /* Override repo mode; for archive-z2 we're looking in the cache, which is in "bare" form */ _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE); if (!checkout_file_hardlink (current_repo, options, loose_path_buf, destination_dfd, destination_name, TRUE, &did_hardlink, cancellable, error)) goto out; if (did_hardlink && options->devino_to_csum_cache) { struct stat stbuf; OstreeDevIno *key; if (TEMP_FAILURE_RETRY (fstatat (destination_dfd, destination_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) { glnx_set_error_from_errno (error); goto out; } key = g_new (OstreeDevIno, 1); key->dev = stbuf.st_dev; key->ino = stbuf.st_ino; memcpy (key->checksum, checksum, OSTREE_SHA256_STRING_LEN+1); g_hash_table_add ((GHashTable*)options->devino_to_csum_cache, key); } if (did_hardlink) break; } current_repo = current_repo->parent_repo; } need_copy = !did_hardlink; } can_cache = (options->enable_uncompressed_cache && repo->enable_uncompressed_cache); /* Ok, if we're archive-z2 and we didn't find an object, uncompress * it now, stick it in the cache, and then hardlink to that. */ if (can_cache && !is_whiteout && !is_symlink && need_copy && repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER) { gboolean did_hardlink; if (!ostree_repo_load_file (repo, checksum, &input, NULL, NULL, cancellable, error)) goto out; /* Overwrite any parent repo from earlier */ _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE); if (!checkout_object_for_uncompressed_cache (repo, loose_path_buf, source_info, input, cancellable, error)) { g_prefix_error (error, "Unpacking loose object %s: ", checksum); goto out; } g_clear_object (&input); /* Store the 2-byte objdir prefix (e.g. e3) in a set. The basic * idea here is that if we had to unpack an object, it's very * likely we're replacing some other object, so we may need a GC. * * This model ensures that we do work roughly proportional to * the size of the changes. For example, we don't scan any * directories if we didn't modify anything, meaning you can * checkout the same tree multiple times very quickly. * * This is also scale independent; we don't hardcode e.g. looking * at 1000 objects. * * The downside is that if we're unlucky, we may not free * an object for quite some time. */ g_mutex_lock (&repo->cache_lock); { gpointer key = GUINT_TO_POINTER ((g_ascii_xdigit_value (checksum[0]) << 4) + g_ascii_xdigit_value (checksum[1])); if (repo->updated_uncompressed_dirs == NULL) repo->updated_uncompressed_dirs = g_hash_table_new (NULL, NULL); g_hash_table_insert (repo->updated_uncompressed_dirs, key, key); } g_mutex_unlock (&repo->cache_lock); if (!checkout_file_hardlink (repo, options, loose_path_buf, destination_dfd, destination_name, FALSE, &did_hardlink, cancellable, error)) { g_prefix_error (error, "Using new cached uncompressed hardlink of %s to %s: ", checksum, destination_name); goto out; } need_copy = !did_hardlink; } /* Fall back to copy if we couldn't hardlink */ if (need_copy) { if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs, cancellable, error)) goto out; if (options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES) { if (!checkout_file_unioning_from_input_at (repo, options, source_info, xattrs, input, destination_dfd, destination_name, cancellable, error)) { g_prefix_error (error, "Union checkout of %s to %s: ", checksum, destination_name); goto out; } } else { if (!checkout_file_from_input_at (repo, options, source_info, xattrs, input, destination_dfd, destination_name, cancellable, error)) { g_prefix_error (error, "Checkout of %s to %s: ", checksum, destination_name); goto out; } } if (input) { if (!g_input_stream_close (input, cancellable, error)) goto out; } } ret = TRUE; out: return ret; }
/* * Iterate over all zvols in a given pool by walking the /dev/zvol/dsk/<pool> * hierarchy. */ int zpool_iter_zvol(zpool_handle_t *zhp, int (*cb)(const char *, void *), void *data) { libzfs_handle_t *hdl = zhp->zpool_hdl; char (*paths)[MAXPATHLEN]; size_t size = 4; int curr, base, ret = 0; // int fd; // DIR *dirp; // struct dirent *dp; struct stat st; if ((base = open("/dev/zvol/dsk", O_RDONLY)) < 0) return (errno == ENOENT ? 0 : -1); #if 0 if (fstatat(base, zhp->zpool_name, &st, 0) != 0) { int err = errno; (void) close(base); return (err == ENOENT ? 0 : -1); } #endif /* * Oddly this wasn't a directory -- ignore that failure since we * know there are no links lower in the (non-existant) hierarchy. */ if (!S_ISDIR(st.st_mode)) { (void) close(base); return (0); } if ((paths = zfs_alloc(hdl, size * sizeof (paths[0]))) == NULL) { (void) close(base); return (-1); } (void) strlcpy(paths[0], zhp->zpool_name, sizeof (paths[0])); curr = 0; #if 0 while (curr >= 0) { if (fstatat(base, paths[curr], &st, AT_SYMLINK_NOFOLLOW) != 0) goto err; if (S_ISDIR(st.st_mode)) { if ((fd = openat(base, paths[curr], O_RDONLY)) < 0) goto err; if ((dirp = fdopendir(fd)) == NULL) { (void) close(fd); goto err; } while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.') continue; if (curr + 1 == size) { paths = zfs_realloc(hdl, paths, size * sizeof (paths[0]), size * 2 * sizeof (paths[0])); if (paths == NULL) { (void) closedir(dirp); (void) close(fd); goto err; } size *= 2; } (void) strlcpy(paths[curr + 1], paths[curr], sizeof (paths[curr + 1])); (void) strlcat(paths[curr], "/", sizeof (paths[curr])); (void) strlcat(paths[curr], dp->d_name, sizeof (paths[curr])); curr++; } (void) closedir(dirp); } else { if ((ret = cb(paths[curr], data)) != 0) break; } curr--; } #endif free(paths); (void) close(base); return (ret); err: free(paths); (void) close(base); return (-1); }
/* remove all files/directories below dirName -- don't cross mountpoints */ static int recursiveRemove(char *dirName) { struct stat rb; DIR *dir; int rc = -1; int dfd; if (!(dir = opendir(dirName))) { warn("failed to open %s", dirName); goto done; } dfd = dirfd(dir); if (fstat(dfd, &rb)) { warn("failed to stat %s", dirName); goto done; } while(1) { struct dirent *d; errno = 0; if (!(d = readdir(dir))) { if (errno) { warn("failed to read %s", dirName); goto done; } break; /* end of directory */ } if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; if (d->d_type == DT_DIR) { struct stat sb; if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { warn("failed to stat %s/%s", dirName, d->d_name); continue; } /* remove subdirectories if device is same as dir */ if (sb.st_dev == rb.st_dev) { char subdir[ strlen(dirName) + strlen(d->d_name) + 2 ]; sprintf(subdir, "%s/%s", dirName, d->d_name); recursiveRemove(subdir); } else continue; } if (unlinkat(dfd, d->d_name, d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) warn("failed to unlink %s/%s", dirName, d->d_name); } rc = 0; /* success */ done: if (dir) closedir(dir); return rc; }
/** * Migrate symbolik links */ bool MigrateFile::migrateSymLink(void) { bool retVal = false; int res; int renameRes; int metaRes; struct stat fromStatData; bool wasModified; int statRes = fstatat(this->dirFD, this->fileName.c_str(), &fromStatData, AT_SYMLINK_NOFOLLOW); if (statRes < 0) { std::cerr << "Failed to stat (" << this->filePath << "): " << System::getErrString() << std::endl; return false; } // + 20 is arbitary, but must be set for the linkTo size test below as well char* linkTo = (char *)malloc(MAX_LINK_SIZE + 20); if (!linkTo) { std::cerr << "malloc failed (" << this->filePath <<"): " << System::getErrString() << std::endl; return false; } // read linkTo and test its size int linkLength = readlinkat(this->dirFD, this->fileName.c_str(), linkTo, MAX_LINK_SIZE + 20); if (linkLength + 1 > MAX_LINK_SIZE ) // +1 as we also need to store the \0 { std::cerr << "Symlink too long: " << this->filePath << std::endl; goto out; } linkTo[linkLength] = '\0'; // we need to add the closing character ourself if (linkLength < 0) { std::cerr << "Failed to read link: " << this->filePath << std::endl; goto out; } res = runCreateIoctl(&fromStatData, linkTo); if (res) { std::cerr << "Failed to create symlink (" << this->filePath << "): " << System::getErrString() << std::endl; goto out; } // only request a mode update if we had to remove some bits when we created the file if (fromStatData.st_mode & (S_ISVTX | S_ISUID | S_ISGID) ) { metaRes = copyOwnerAndModeLink(&fromStatData); // mode and owner if (metaRes == false) { removeTmpOnErr(); goto out; } } /* Just before the rename and to keep a possible race window a short as possible, we do a * sanity check, if the file was changed in the mean time */ wasModified = this->fileWasModified(&fromStatData); if (wasModified) { removeTmpOnErr(); goto out; } renameRes = renameat(this->dirFD, this->tmpName.c_str(), this->dirFD, this->fileName.c_str()); if (renameRes) { std::cerr << "Renaming tmpFile failed (" << this->tmpName << "->" << this->filePath << "): " << System::getErrString() << std::endl; removeTmpOnErr(); goto out; } // now copy over the time stamps, NOTE: although we renamed the file, tmpFD is still valid. metaRes = copyTimesLink(&fromStatData); if (metaRes == false) { removeTmpOnErr(); goto out; } retVal = true; out: free(linkTo); return retVal; }
int main(int argc, char *argv[]) { struct stat sb; struct timespec ts[2]; int atflag; int Aflag, aflag, cflag, mflag, ch, fd, len, rval, timeset; char *p; char *myname; #ifdef SHELL char *optarg; #endif myname = basename(argv[0]); Aflag = aflag = cflag = mflag = timeset = 0; atflag = 0; ts[0].tv_sec = ts[1].tv_sec = 0; ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW; #ifdef SHELL while ((ch = nextopt("A:acd:fhmr:t:")) != '\0') { optarg = shoptarg; #else while ((ch = getopt(argc, argv, "A:acd:fhmr:t:")) != -1) #endif switch(ch) { case 'A': Aflag = timeoffset(optarg); break; case 'a': aflag = 1; break; case 'c': cflag = 1; break; case 'd': timeset = 1; stime_darg(optarg, ts); break; case 'f': /* No-op for compatibility. */ break; case 'h': cflag = 1; atflag = AT_SYMLINK_NOFOLLOW; break; case 'm': mflag = 1; break; case 'r': timeset = 1; stime_file(optarg, ts); break; case 't': timeset = 1; stime_arg1(optarg, ts); break; default: usage(myname); } #ifdef SHELL } argc -= argptr - argv; argv = argptr; #else argc -= optind; argv += optind; #endif if (aflag == 0 && mflag == 0) aflag = mflag = 1; if (timeset) { if (Aflag) { /* * We're setting the time to an offset from a specified * time. God knows why, but it means that we can set * that time once and for all here. */ if (aflag) ts[0].tv_sec += Aflag; if (mflag) ts[1].tv_sec += Aflag; Aflag = 0; /* done our job */ } } else { /* * If no -r or -t flag, at least two operands, the first of * which is an 8 or 10 digit number, use the obsolete time * specification, otherwise use the current time. */ if (argc > 1) { strtol(argv[0], &p, 10); len = p - argv[0]; if (*p == '\0' && (len == 8 || len == 10)) { timeset = 1; stime_arg2(*argv++, len == 10, ts); } } /* Both times default to the same. */ ts[1] = ts[0]; } if (!aflag) ts[0].tv_nsec = UTIME_OMIT; if (!mflag) ts[1].tv_nsec = UTIME_OMIT; if (*argv == NULL) usage(myname); if (Aflag) cflag = 1; for (rval = 0; *argv; ++argv) { /* See if the file exists. */ if (fstatat(AT_FDCWD, *argv, &sb, atflag) != 0) { if (errno != ENOENT) { rval = 1; warn("%s", *argv); continue; } if (!cflag) { /* Create the file. */ fd = open(*argv, O_WRONLY | O_CREAT, DEFFILEMODE); if (fd == -1 || fstat(fd, &sb) || close(fd)) { rval = 1; warn("%s", *argv); continue; } /* If using the current time, we're done. */ if (!timeset) continue; } else continue; } /* * We're adjusting the times based on the file times, not a * specified time (that gets handled above). */ if (Aflag) { if (aflag) { ts[0] = sb.st_atim; ts[0].tv_sec += Aflag; } if (mflag) { ts[1] = sb.st_mtim; ts[1].tv_sec += Aflag; } } if (!utimensat(AT_FDCWD, *argv, ts, atflag)) continue; rval = 1; warn("%s", *argv); } return (rval); }
char * __getcwd (char *buf, size_t size) { /* Lengths of big file name components and entire file names, and a deep level of file name nesting. These numbers are not upper bounds; they are merely large values suitable for initial allocations, designed to be large enough for most real-world uses. */ enum { BIG_FILE_NAME_COMPONENT_LENGTH = 255, BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1), DEEP_NESTING = 100 }; #ifdef AT_FDCWD int fd = AT_FDCWD; bool fd_needs_closing = false; #else char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1]; char *dotlist = dots; size_t dotsize = sizeof dots; size_t dotlen = 0; #endif DIR *dirstream = NULL; dev_t rootdev, thisdev; ino_t rootino, thisino; char *dir; register char *dirp; struct stat st; size_t allocated = size; size_t used; #if HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD /* The system getcwd works, except it sometimes fails when it shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. If AT_FDCWD is not defined, the algorithm below is O(N**2) and this is much slower than the system getcwd (at least on GNU/Linux). So trust the system getcwd's results unless they look suspicious. */ # undef getcwd dir = getcwd (buf, size); if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT)) return dir; #endif if (size == 0) { if (buf != NULL) { __set_errno (EINVAL); return NULL; } allocated = BIG_FILE_NAME_LENGTH + 1; } if (buf == NULL) { dir = malloc (allocated); if (dir == NULL) return NULL; } else dir = buf; dirp = dir + allocated; *--dirp = '\0'; if (__lstat (".", &st) < 0) goto lose; thisdev = st.st_dev; thisino = st.st_ino; if (__lstat ("/", &st) < 0) goto lose; rootdev = st.st_dev; rootino = st.st_ino; while (!(thisdev == rootdev && thisino == rootino)) { struct dirent *d; dev_t dotdev; ino_t dotino; bool mount_point; int parent_status; size_t dirroom; size_t namlen; /* Look at the parent directory. */ #ifdef AT_FDCWD fd = openat (fd, "..", O_RDONLY); if (fd < 0) goto lose; fd_needs_closing = true; parent_status = fstat (fd, &st); #else dotlist[dotlen++] = '.'; dotlist[dotlen++] = '.'; dotlist[dotlen] = '\0'; parent_status = __lstat (dotlist, &st); #endif if (parent_status != 0) goto lose; if (dirstream && __closedir (dirstream) != 0) { dirstream = NULL; goto lose; } /* Figure out if this directory is a mount point. */ dotdev = st.st_dev; dotino = st.st_ino; mount_point = dotdev != thisdev; /* Search for the last directory. */ #ifdef AT_FDCWD dirstream = fdopendir (fd); if (dirstream == NULL) goto lose; fd_needs_closing = false; #else dirstream = __opendir (dotlist); if (dirstream == NULL) goto lose; dotlist[dotlen++] = '/'; #endif for (;;) { /* Clear errno to distinguish EOF from error if readdir returns NULL. */ __set_errno (0); d = __readdir (dirstream); if (d == NULL) { if (errno == 0) /* EOF on dirstream, which means that the current directory has been removed. */ __set_errno (ENOENT); goto lose; } if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) continue; if (MATCHING_INO (d, thisino) || mount_point) { int entry_status; #ifdef AT_FDCWD entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW); #else /* Compute size needed for this file name, or for the file name ".." in the same directory, whichever is larger. Room for ".." might be needed the next time through the outer loop. */ size_t name_alloc = _D_ALLOC_NAMLEN (d); size_t filesize = dotlen + MAX (sizeof "..", name_alloc); if (filesize < dotlen) goto memory_exhausted; if (dotsize < filesize) { /* My, what a deep directory tree you have, Grandma. */ size_t newsize = MAX (filesize, dotsize * 2); size_t i; if (newsize < dotsize) goto memory_exhausted; if (dotlist != dots) free (dotlist); dotlist = malloc (newsize); if (dotlist == NULL) goto lose; dotsize = newsize; i = 0; do { dotlist[i++] = '.'; dotlist[i++] = '.'; dotlist[i++] = '/'; } while (i < dotlen); } strcpy (dotlist + dotlen, d->d_name); entry_status = __lstat (dotlist, &st); #endif /* We don't fail here if we cannot stat() a directory entry. This can happen when (network) file systems fail. If this entry is in fact the one we are looking for we will find out soon as we reach the end of the directory without having found anything. */ if (entry_status == 0 && S_ISDIR (st.st_mode) && st.st_dev == thisdev && st.st_ino == thisino) break; } } dirroom = dirp - dir; namlen = _D_EXACT_NAMLEN (d); if (dirroom <= namlen) { if (size != 0) { __set_errno (ERANGE); goto lose; } else { char *tmp; size_t oldsize = allocated; allocated += MAX (allocated, namlen); if (allocated < oldsize || ! (tmp = realloc (dir, allocated))) goto memory_exhausted; /* Move current contents up to the end of the buffer. This is guaranteed to be non-overlapping. */ dirp = memcpy (tmp + allocated - (oldsize - dirroom), tmp + dirroom, oldsize - dirroom); dir = tmp; } } dirp -= namlen; memcpy (dirp, d->d_name, namlen); *--dirp = '/'; thisdev = dotdev; thisino = dotino; } if (dirstream && __closedir (dirstream) != 0) { dirstream = NULL; goto lose; } if (dirp == &dir[allocated - 1]) *--dirp = '/'; #ifndef AT_FDCWD if (dotlist != dots) free (dotlist); #endif used = dir + allocated - dirp; memmove (dir, dirp, used); if (buf == NULL && size == 0) /* Ensure that the buffer is only as large as necessary. */ buf = realloc (dir, used); if (buf == NULL) /* Either buf was NULL all along, or `realloc' failed but we still have the original string. */ buf = dir; return buf; memory_exhausted: __set_errno (ENOMEM); lose: { int save = errno; if (dirstream) __closedir (dirstream); #ifdef AT_FDCWD if (fd_needs_closing) close (fd); #else if (dotlist != dots) free (dotlist); #endif if (buf == NULL) free (dir); __set_errno (save); } return NULL; }
static int dir_cleanup( Item *i, const char *p, DIR *d, const struct stat *ds, usec_t cutoff, dev_t rootdev, bool mountpoint, int maxdepth, bool keep_this_level) { struct dirent *dent; struct timespec times[2]; bool deleted = false; int r = 0; while ((dent = readdir(d))) { struct stat s; usec_t age; char _cleanup_free_ *sub_path = NULL; if (streq(dent->d_name, ".") || streq(dent->d_name, "..")) continue; if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { if (errno != ENOENT) { log_error("stat(%s/%s) failed: %m", p, dent->d_name); r = -errno; } continue; } /* Stay on the same filesystem */ if (s.st_dev != rootdev) continue; /* Try to detect bind mounts of the same filesystem instance; they * do not differ in device major/minors. This type of query is not * supported on all kernels or filesystem types though. */ if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) continue; /* Do not delete read-only files owned by root */ if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) continue; if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) { r = log_oom(); goto finish; } /* Is there an item configured for this path? */ if (hashmap_get(items, sub_path)) continue; if (find_glob(globs, sub_path)) continue; if (S_ISDIR(s.st_mode)) { if (mountpoint && streq(dent->d_name, "lost+found") && s.st_uid == 0) continue; if (maxdepth <= 0) log_warning("Reached max depth on %s.", sub_path); else { DIR _cleanup_closedir_ *sub_dir; int q; sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME); if (sub_dir == NULL) { if (errno != ENOENT) { log_error("opendir(%s/%s) failed: %m", p, dent->d_name); r = -errno; } continue; } q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); if (q < 0) r = q; } /* Note: if you are wondering why we don't * support the sticky bit for excluding * directories from cleaning like we do it for * other file system objects: well, the sticky * bit already has a meaning for directories, * so we don't want to overload that. */ if (keep_this_level) continue; /* Ignore ctime, we change it when deleting */ age = MAX(timespec_load(&s.st_mtim), timespec_load(&s.st_atim)); if (age >= cutoff) continue; if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) { log_debug("rmdir '%s'\n", sub_path); if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) { if (errno != ENOENT && errno != ENOTEMPTY) { log_error("rmdir(%s): %m", sub_path); r = -errno; } } } } else { /* Skip files for which the sticky bit is * set. These are semantics we define, and are * unknown elsewhere. See XDG_RUNTIME_DIR * specification for details. */ if (s.st_mode & S_ISVTX) continue; if (mountpoint && S_ISREG(s.st_mode)) { if (streq(dent->d_name, ".journal") && s.st_uid == 0) continue; if (streq(dent->d_name, "aquota.user") || streq(dent->d_name, "aquota.group")) continue; } /* Ignore sockets that are listed in /proc/net/unix */ if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) continue; /* Ignore device nodes */ if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) continue; /* Keep files on this level around if this is * requested */ if (keep_this_level) continue; age = MAX3(timespec_load(&s.st_mtim), timespec_load(&s.st_atim), timespec_load(&s.st_ctim)); if (age >= cutoff) continue; log_debug("unlink '%s'\n", sub_path); if (unlinkat(dirfd(d), dent->d_name, 0) < 0) { if (errno != ENOENT) { log_error("unlink(%s): %m", sub_path); r = -errno; } } deleted = true; } } finish: if (deleted) { /* Restore original directory timestamps */ times[0] = ds->st_atim; times[1] = ds->st_mtim; if (futimens(dirfd(d), times) < 0) log_error("utimensat(%s): %m", p); } return r; }
int main (void) { int i; test_func funcs[2] = { mkfifoat, test_mknodat }; int result; /* Remove any leftovers from a previous partial run. */ ignore_value (system ("rm -rf " BASE "*")); /* Basic tests. */ result = test_mkfifo (do_mkfifoat, true); ASSERT (test_mkfifo (do_mknodat, false) == result); dfd = open (".", O_RDONLY); ASSERT (0 <= dfd); ASSERT (test_mkfifo (do_mkfifoat, false) == result); ASSERT (test_mkfifo (do_mknodat, false) == result); /* Test directory-relative handling of both functions. */ for (i = 0; i < 2; i++) { struct stat st; test_func func = funcs[i]; /* Test behaviour for invalid file descriptors. */ { errno = 0; ASSERT (func (-1, "foo", 0600) == -1); ASSERT (errno == EBADF || errno == ENOSYS /* seen on mingw */ ); } { errno = 0; ASSERT (func (99, "foo", 0600) == -1); ASSERT (errno == EBADF || errno == ENOSYS /* seen on mingw */ ); } /* Create fifo while cwd is '.', then stat it from '..'. */ if (func (AT_FDCWD, BASE "fifo", 0600) != 0) ASSERT (errno == ENOSYS); /* seen on native Windows */ else { errno = 0; ASSERT (func (dfd, BASE "fifo", 0600) == -1); ASSERT (errno == EEXIST); ASSERT (chdir ("..") == 0); errno = 0; ASSERT (fstatat (AT_FDCWD, BASE "fifo", &st, 0) == -1); ASSERT (errno == ENOENT); memset (&st, 0, sizeof st); ASSERT (fstatat (dfd, BASE "fifo", &st, 0) == 0); ASSERT (S_ISFIFO (st.st_mode)); ASSERT (unlinkat (dfd, BASE "fifo", 0) == 0); } /* Create fifo while cwd is '..', then stat it from '.'. */ if (func (dfd, BASE "fifo", 0600) != 0) ASSERT (errno == ENOSYS); /* seen on native Windows */ else { ASSERT (fchdir (dfd) == 0); errno = 0; ASSERT (func (AT_FDCWD, BASE "fifo", 0600) == -1); ASSERT (errno == EEXIST); memset (&st, 0, sizeof st); ASSERT (fstatat (AT_FDCWD, BASE "fifo", &st, AT_SYMLINK_NOFOLLOW) == 0); ASSERT (S_ISFIFO (st.st_mode)); memset (&st, 0, sizeof st); ASSERT (fstatat (dfd, BASE "fifo", &st, AT_SYMLINK_NOFOLLOW) == 0); ASSERT (S_ISFIFO (st.st_mode)); ASSERT (unlink (BASE "fifo") == 0); } } ASSERT (close (dfd) == 0); return 0; }
int main() { int fd; char cwd[128]; struct stat sbuf; struct utimbuf ubuf; getcwd(cwd, 128); //staptest// getcwd (XXXX, 128) = NNNN fd = creat("foobar", S_IREAD|S_IWRITE); //staptest// [[[[open ("foobar", O_WRONLY|O_CREAT[[[[.O_LARGEFILE]]]]?|O_TRUNC!!!!creat ("foobar"!!!!openat (AT_FDCWD, "foobar", O_WRONLY|O_CREAT|O_TRUNC]]]], 0600) = NNNN fstat(fd, &sbuf); //staptest// fstat (NNNN, XXXX) = 0 fstat(-1, &sbuf); //staptest// fstat (-1, XXXX) = -NNNN (EBADF) // Here we specify -1 to both arguments, to avoid a SIGSEGV. fstat(-1, (struct stat *)-1); #if __WORDSIZE != 64 // Notice we're not checking for 0x[f]+ here for the 2nd // argument. On RHEL[67] {x86_64,s390x}, for a 32-bit exe, glibc // substituted a real structure address (verified with strace). // staptest// fstat (-1, XXXX) = -NNNN #else //staptest// fstat (-1, 0x[f]+) = -NNNN #endif close(fd); stat("foobar", &sbuf); //staptest// [[[[stat ("foobar", XXXX!!!!fstatat (AT_FDCWD, "foobar", XXXX, 0x0]]]]) = 0 stat((char *)-1, &sbuf); #if defined(__s390__) //staptest// stat (0x[7]?[f]+, XXXX) = -NNNN #else //staptest// [[[[stat (0x[f]+, XXXX!!!!fstatat (AT_FDCWD, 0x[f]+, XXXX, 0x0]]]]) = -NNNN #endif // Here we specify -1 to both arguments, to avoid a SIGSEGV. stat((char *)-1, (struct stat *)-1); #if __WORDSIZE != 64 // Notice we're not checking for 0x[f]+ here for the 2nd // argument. On RHEL[67] {x86_64,s390x}, for a 32-bit exe, glibc // substituted a real structure address (verified with strace). //staptest// stat (0x[7]?[f]+, XXXX) = -NNNN #else //staptest// [[[[stat (0x[f]+, 0x[f]+!!!!fstatat (AT_FDCWD, 0x[f]+, 0x[f]+, 0x0]]]]) = -NNNN #endif lstat("foobar", &sbuf); //staptest// [[[[lstat ("foobar", XXXX!!!!fstatat (AT_FDCWD, "foobar", XXXX, AT_SYMLINK_NOFOLLOW]]]]) = 0 lstat((char *)-1, &sbuf); #if defined(__s390__) //staptest// lstat (0x[7]?[f]+, XXXX) = -NNNN (EFAULT) #else //staptest// [[[[lstat (0x[f]+, XXXX!!!!fstatat (AT_FDCWD, 0x[f]+, XXXX, AT_SYMLINK_NOFOLLOW]]]]) = -NNNN (EFAULT) #endif // Here we specify -1 to both arguments, to avoid a SIGSEGV. lstat((char *)-1, (struct stat *)-1); #if __WORDSIZE != 64 // Notice we're not checking for 0x[f]+ here for the 2nd // argument. On RHEL[67] {x86_64,s390x}, for a 32-bit exe, glibc // substituted a real structure address (verified with strace). //staptest// lstat (0x[7]?[f]+, XXXX) = -NNNN #else //staptest// [[[[lstat (0x[f]+, 0x[f]+!!!!fstatat (AT_FDCWD, 0x[f]+, 0x[f]+, AT_SYMLINK_NOFOLLOW]]]]) = -NNNN #endif #if GLIBC_SUPPORT fstatat(AT_FDCWD, "foobar", &sbuf, AT_SYMLINK_NOFOLLOW); //staptest// fstatat (AT_FDCWD, "foobar", XXXX, AT_SYMLINK_NOFOLLOW) = 0 fstatat(-1, "foobar", &sbuf, AT_SYMLINK_NOFOLLOW); //staptest// fstatat (-1, "foobar", XXXX, AT_SYMLINK_NOFOLLOW) = -NNNN (EBADF) fstatat(AT_FDCWD, (char *)-1, &sbuf, AT_SYMLINK_NOFOLLOW); #if defined(__s390__) //staptest// fstatat (AT_FDCWD, 0x[7]?[f]+, XXXX, AT_SYMLINK_NOFOLLOW) = -NNNN (EFAULT) #else //staptest// fstatat (AT_FDCWD, 0x[f]+, XXXX, AT_SYMLINK_NOFOLLOW) = -NNNN (EFAULT) #endif // Try to avoid a SIGSEGV. fstatat(-1, "foobar", (struct stat *)-1, AT_SYMLINK_NOFOLLOW); #if __WORDSIZE != 64 // Notice we're not checking for 0x[f]+ here for the 3rd // argument. On RHEL[67] {x86_64,s390x}, for a 32-bit exe, glibc // substituted a real structure address (verified with strace). //staptest// fstatat (-1, "foobar", XXXX, AT_SYMLINK_NOFOLLOW) = -NNNN #else //staptest// fstatat (-1, "foobar", 0x[f]+, AT_SYMLINK_NOFOLLOW) = -NNNN #endif fstatat(AT_FDCWD, "foobar", &sbuf, -1); //staptest// fstatat (AT_FDCWD, "foobar", XXXX, AT_[^ ]+|XXXX) = -NNNN (EINVAL) #endif ubuf.actime = 1; ubuf.modtime = 1135641600; utime("foobar", &ubuf); #if defined(__aarch64__) //staptest// utimensat (AT_FDCWD, "foobar", \[1.[0]+\]\[1135641600.[0]+\], 0x0) = #elif defined(__ia64__) || defined(__arm__) //staptest// utimes ("foobar", \[1.000000\]\[1135641600.000000\]) = #else //staptest// utime ("foobar", \[Thu Jan 1 00:00:01 1970, Tue Dec 27 00:00:00 2005\]) = 0 #endif ubuf.actime = 1135690000; ubuf.modtime = 1135700000; utime("foobar", &ubuf); #if defined(__aarch64__) //staptest// utimensat (AT_FDCWD, "foobar", \[1135690000.[0]+\]\[1135700000.[0]+\], 0x0) = #elif defined(__ia64__) || defined(__arm__) //staptest// utimes ("foobar", \[1135690000.000000\]\[1135700000.000000\]) = #else //staptest// utime ("foobar", \[Tue Dec 27 13:26:40 2005, Tue Dec 27 16:13:20 2005\]) = 0 #endif ubuf.actime = 1; ubuf.modtime = 1135641600; utime((char *)-1, &ubuf); #if defined(__aarch64__) //staptest// utimensat (AT_FDCWD, 0x[f]+, \[1.[0]+\]\[1135641600.[0]+\], 0x0) = -NNNN #elif defined(__ia64__) || defined(__arm__) //staptest// utimes (0x[f]+, \[1.000000\]\[1135641600.000000\]) = -NNNN #elif defined(__s390__) //staptest// utime (0x[7]?[f]+, \[Thu Jan 1 00:00:01 1970, Tue Dec 27 00:00:00 2005\]) = -NNNN #else //staptest// utime (0x[f]+, \[Thu Jan 1 00:00:01 1970, Tue Dec 27 00:00:00 2005\]) = -NNNN #endif #if defined(__ia64__) || defined(__arm__) || defined(__aarch64__) // Avoid a SIGSEGV by specifying NULL, not -1. utime("foobar", (struct utimbuf *)NULL); //staptest// [[[[utimes ("foobar", NULL!!!!utimensat (AT_FDCWD, "foobar", NULL, 0x0]]]]) = NNNN #else utime("foobar", (struct utimbuf *)-1); //staptest// utime ("foobar", \[Thu Jan 1 00:00:00 1970, Thu Jan 1 00:00:00 1970\]) = -NNNN #endif return 0; }
int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, const char *asecpath, int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int64_t* _asecsize) { DIR *d; int dfd; struct dirent *de; struct stat s; char path[PKG_PATH_MAX]; int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; int64_t asecsize = 0; /* count the source apk as code -- but only if it's not * on the /system partition and its not on the sdcard. */ if (validate_system_app_path(apkpath) && strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) { if (stat(apkpath, &s) == 0) { codesize += stat_size(&s); } } /* count the forward locked apk as code if it is given */ if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') { if (stat(fwdlock_apkpath, &s) == 0) { codesize += stat_size(&s); } } /* count the cached dexfile as code */ if (!create_cache_path(path, apkpath)) { if (stat(path, &s) == 0) { codesize += stat_size(&s); } } /* compute asec size if it is given */ if (asecpath != NULL && asecpath[0] != '!') { if (stat(asecpath, &s) == 0) { asecsize += stat_size(&s); } } if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) { goto done; } d = opendir(path); if (d == NULL) { goto done; } dfd = dirfd(d); /* most stuff in the pkgdir is data, except for the "cache" * directory and below, which is cache, and the "lib" directory * and below, which is code... */ while ((de = readdir(d))) { const char *name = de->d_name; if (de->d_type == DT_DIR) { int subfd; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (subfd >= 0) { int64_t size = calculate_dir_size(subfd); if (!strcmp(name,"lib")) { codesize += size; } else if(!strcmp(name,"cache")) { cachesize += size; } else { datasize += size; } } } else { if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { datasize += stat_size(&s); } } } closedir(d); done: *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; *_asecsize = asecsize; return 0; }
char * __getcwd (char *buf, size_t size) { /* Lengths of big file name components and entire file names, and a deep level of file name nesting. These numbers are not upper bounds; they are merely large values suitable for initial allocations, designed to be large enough for most real-world uses. */ enum { BIG_FILE_NAME_COMPONENT_LENGTH = 255, BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1), DEEP_NESTING = 100 }; #if HAVE_OPENAT_SUPPORT int fd = AT_FDCWD; bool fd_needs_closing = false; #else char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1]; char *dotlist = dots; size_t dotsize = sizeof dots; size_t dotlen = 0; #endif DIR *dirstream = NULL; dev_t rootdev, thisdev; ino_t rootino, thisino; char *dir; register char *dirp; struct stat st; size_t allocated = size; size_t used; #if HAVE_MINIMALLY_WORKING_GETCWD /* If AT_FDCWD is not defined, the algorithm below is O(N**2) and this is much slower than the system getcwd (at least on GNU/Linux). So trust the system getcwd's results unless they look suspicious. Use the system getcwd even if we have openat support, since the system getcwd works even when a parent is unreadable, while the openat-based approach does not. But on AIX 5.1..7.1, the system getcwd is not even minimally working: If the current directory name is slightly longer than PATH_MAX, it omits the first directory component and returns this wrong result with errno = 0. */ # undef getcwd dir = getcwd (buf, size); if (dir || (size && errno == ERANGE)) return dir; /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has internal magic that lets it work even if an ancestor directory is inaccessible, which is better in many cases. So in this case try again with a buffer that's almost always big enough. */ if (errno == EINVAL && buf == NULL && size == 0) { char big_buffer[BIG_FILE_NAME_LENGTH + 1]; dir = getcwd (big_buffer, sizeof big_buffer); if (dir) return strdup (dir); } # if HAVE_PARTLY_WORKING_GETCWD /* The system getcwd works, except it sometimes fails when it shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. */ if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT) return NULL; # endif #endif if (size == 0) { if (buf != NULL) { __set_errno (EINVAL); return NULL; } allocated = BIG_FILE_NAME_LENGTH + 1; } if (buf == NULL) { dir = malloc (allocated); if (dir == NULL) return NULL; } else dir = buf; dirp = dir + allocated; *--dirp = '\0'; if (__lstat (".", &st) < 0) goto lose; thisdev = st.st_dev; thisino = st.st_ino; if (__lstat ("/", &st) < 0) goto lose; rootdev = st.st_dev; rootino = st.st_ino; while (!(thisdev == rootdev && thisino == rootino)) { struct dirent *d; dev_t dotdev; ino_t dotino; bool mount_point; int parent_status; size_t dirroom; size_t namlen; bool use_d_ino = true; /* Look at the parent directory. */ #if HAVE_OPENAT_SUPPORT fd = openat (fd, "..", O_RDONLY); if (fd < 0) goto lose; fd_needs_closing = true; parent_status = fstat (fd, &st); #else dotlist[dotlen++] = '.'; dotlist[dotlen++] = '.'; dotlist[dotlen] = '\0'; parent_status = __lstat (dotlist, &st); #endif if (parent_status != 0) goto lose; if (dirstream && __closedir (dirstream) != 0) { dirstream = NULL; goto lose; } /* Figure out if this directory is a mount point. */ dotdev = st.st_dev; dotino = st.st_ino; mount_point = dotdev != thisdev; /* Search for the last directory. */ #if HAVE_OPENAT_SUPPORT dirstream = fdopendir (fd); if (dirstream == NULL) goto lose; fd_needs_closing = false; #else dirstream = __opendir (dotlist); if (dirstream == NULL) goto lose; dotlist[dotlen++] = '/'; #endif for (;;) { /* Clear errno to distinguish EOF from error if readdir returns NULL. */ __set_errno (0); d = __readdir (dirstream); /* When we've iterated through all directory entries without finding one with a matching d_ino, rewind the stream and consider each name again, but this time, using lstat. This is necessary in a chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where .., ../.., ../../.., etc. all had the same device number, yet the d_ino values for entries in / did not match those obtained via lstat. */ if (d == NULL && errno == 0 && use_d_ino) { use_d_ino = false; rewinddir (dirstream); d = __readdir (dirstream); } if (d == NULL) { if (errno == 0) /* EOF on dirstream, which can mean e.g., that the current directory has been removed. */ __set_errno (ENOENT); goto lose; } if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) continue; if (use_d_ino) { bool match = (MATCHING_INO (d, thisino) || mount_point); if (! match) continue; } { int entry_status; #if HAVE_OPENAT_SUPPORT entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW); #else /* Compute size needed for this file name, or for the file name ".." in the same directory, whichever is larger. Room for ".." might be needed the next time through the outer loop. */ size_t name_alloc = _D_ALLOC_NAMLEN (d); size_t filesize = dotlen + MAX (sizeof "..", name_alloc); if (filesize < dotlen) goto memory_exhausted; if (dotsize < filesize) { /* My, what a deep directory tree you have, Grandma. */ size_t newsize = MAX (filesize, dotsize * 2); size_t i; if (newsize < dotsize) goto memory_exhausted; if (dotlist != dots) free (dotlist); dotlist = malloc (newsize); if (dotlist == NULL) goto lose; dotsize = newsize; i = 0; do { dotlist[i++] = '.'; dotlist[i++] = '.'; dotlist[i++] = '/'; } while (i < dotlen); } memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d)); entry_status = __lstat (dotlist, &st); #endif /* We don't fail here if we cannot stat() a directory entry. This can happen when (network) file systems fail. If this entry is in fact the one we are looking for we will find out soon as we reach the end of the directory without having found anything. */ if (entry_status == 0 && S_ISDIR (st.st_mode) && st.st_dev == thisdev && st.st_ino == thisino) break; } } dirroom = dirp - dir; namlen = _D_EXACT_NAMLEN (d); if (dirroom <= namlen) { if (size != 0) { __set_errno (ERANGE); goto lose; } else { char *tmp; size_t oldsize = allocated; allocated += MAX (allocated, namlen); if (allocated < oldsize || ! (tmp = realloc (dir, allocated))) goto memory_exhausted; /* Move current contents up to the end of the buffer. This is guaranteed to be non-overlapping. */ dirp = memcpy (tmp + allocated - (oldsize - dirroom), tmp + dirroom, oldsize - dirroom); dir = tmp; } } dirp -= namlen; memcpy (dirp, d->d_name, namlen); *--dirp = '/'; thisdev = dotdev; thisino = dotino; } if (dirstream && __closedir (dirstream) != 0) { dirstream = NULL; goto lose; } if (dirp == &dir[allocated - 1]) *--dirp = '/'; #if ! HAVE_OPENAT_SUPPORT if (dotlist != dots) free (dotlist); #endif used = dir + allocated - dirp; memmove (dir, dirp, used); if (size == 0) /* Ensure that the buffer is only as large as necessary. */ buf = realloc (dir, used); if (buf == NULL) /* Either buf was NULL all along, or 'realloc' failed but we still have the original string. */ buf = dir; return buf; memory_exhausted: __set_errno (ENOMEM); lose: { int save = errno; if (dirstream) __closedir (dirstream); #if HAVE_OPENAT_SUPPORT if (fd_needs_closing) close (fd); #else if (dotlist != dots) free (dotlist); #endif if (buf == NULL) free (dir); __set_errno (save); } return NULL; }
static gboolean find_booted_deployment (OstreeSysroot *self, GPtrArray *deployments, OstreeDeployment **out_deployment, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; struct stat root_stbuf; struct stat self_stbuf; glnx_unref_object OstreeDeployment *ret_deployment = NULL; if (stat ("/", &root_stbuf) != 0) { glnx_set_error_from_errno (error); goto out; } if (!ensure_sysroot_fd (self, error)) goto out; if (fstat (self->sysroot_fd, &self_stbuf) != 0) { glnx_set_error_from_errno (error); goto out; } if (root_stbuf.st_dev == self_stbuf.st_dev && root_stbuf.st_ino == self_stbuf.st_ino) { guint i; const char *bootlink_arg; __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kernel_args = NULL; if (!parse_kernel_commandline (&kernel_args, cancellable, error)) goto out; bootlink_arg = _ostree_kernel_args_get_last_value (kernel_args, "ostree"); if (bootlink_arg) { for (i = 0; i < deployments->len; i++) { OstreeDeployment *deployment = deployments->pdata[i]; g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); struct stat stbuf; if (fstatat (self->sysroot_fd, deployment_path, &stbuf, 0) != 0) { glnx_set_error_from_errno (error); goto out; } if (stbuf.st_dev == root_stbuf.st_dev && stbuf.st_ino == root_stbuf.st_ino) { ret_deployment = g_object_ref (deployment); break; } } if (ret_deployment == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected state: ostree= kernel argument found, but / is not a deployment root"); goto out; } } else { /* Not an ostree system */ } } ret = TRUE; ot_transfer_out_value (out_deployment, &ret_deployment); out: return ret; }
static int fd_copy_directory( int df, const char *from, const struct stat *st, int dt, const char *to, dev_t original_device, bool merge) { _cleanup_close_ int fdf = -1, fdt = -1; _cleanup_closedir_ DIR *d = NULL; struct dirent *de; bool created; int r; assert(st); assert(to); if (from) fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); else fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); d = fdopendir(fdf); if (!d) return -errno; fdf = -1; r = mkdirat(dt, to, st->st_mode & 07777); if (r >= 0) created = true; else if (errno == EEXIST && merge) created = false; else return -errno; fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fdt < 0) return -errno; r = 0; if (created) { if (fchown(fdt, st->st_uid, st->st_gid) < 0) r = -errno; if (fchmod(fdt, st->st_mode & 07777) < 0) r = -errno; } FOREACH_DIRENT(de, d, return -errno) { struct stat buf; int q; if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { r = -errno; continue; } if (buf.st_dev != original_device) continue; if (S_ISREG(buf.st_mode)) q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISDIR(buf.st_mode)) q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); else if (S_ISLNK(buf.st_mode)) q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISFIFO(buf.st_mode)) q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); else q = -ENOTSUP; if (q == -EEXIST && merge) q = 0; if (q < 0) r = q; } return r; }
gboolean _ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self, int bootversion, GPtrArray **out_loader_configs, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int fd; /* Temporary owned by iterator */ g_autofree char *entries_path = g_strdup_printf ("boot/loader.%d/entries", bootversion); g_autoptr(GPtrArray) ret_loader_configs = NULL; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!ensure_sysroot_fd (self, error)) goto out; ret_loader_configs = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); fd = glnx_opendirat_with_errno (self->sysroot_fd, entries_path, TRUE); if (fd == -1) { if (errno == ENOENT) goto done; else { glnx_set_error_from_errno (error); goto out; } } if (!glnx_dirfd_iterator_init_take_fd (fd, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent; struct stat stbuf; if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, 0) != 0) { glnx_set_error_from_errno (error); goto out; } if (g_str_has_prefix (dent->d_name, "ostree-") && g_str_has_suffix (dent->d_name, ".conf") && S_ISREG (stbuf.st_mode)) { glnx_unref_object OstreeBootconfigParser *config = ostree_bootconfig_parser_new (); if (!ostree_bootconfig_parser_parse_at (config, dfd_iter.fd, dent->d_name, cancellable, error)) { g_prefix_error (error, "Parsing %s: ", dent->d_name); goto out; } g_ptr_array_add (ret_loader_configs, g_object_ref (config)); } } /* Callers expect us to give them a sorted array */ g_ptr_array_sort (ret_loader_configs, compare_loader_configs_for_sorting); done: gs_transfer_out_value (out_loader_configs, &ret_loader_configs); ret = TRUE; out: return ret; }
/** * ostree_repo_list_static_delta_names: * @self: Repo * @out_deltas: (out) (element-type utf8) (transfer container): String name of deltas (checksum-checksum.delta) * @cancellable: Cancellable * @error: Error * * This function synchronously enumerates all static deltas in the * repository, returning its result in @out_deltas. */ gboolean ostree_repo_list_static_delta_names (OstreeRepo *self, GPtrArray **out_deltas, GCancellable *cancellable, GError **error) { g_autoptr(GPtrArray) ret_deltas = g_ptr_array_new_with_free_func (g_free); g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; gboolean exists; if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "deltas", &dfd_iter, &exists, error)) return FALSE; if (!exists) { /* Note early return */ ot_transfer_out_value (out_deltas, &ret_deltas); return TRUE; } while (TRUE) { g_auto(GLnxDirFdIterator) sub_dfd_iter = { 0, }; struct dirent *dent; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) return FALSE; if (dent == NULL) break; if (dent->d_type != DT_DIR) continue; if (!glnx_dirfd_iterator_init_at (dfd_iter.fd, dent->d_name, FALSE, &sub_dfd_iter, error)) return FALSE; while (TRUE) { struct dirent *sub_dent; const char *name1; const char *name2; g_autofree char *superblock_subpath = NULL; struct stat stbuf; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&sub_dfd_iter, &sub_dent, cancellable, error)) return FALSE; if (sub_dent == NULL) break; if (dent->d_type != DT_DIR) continue; name1 = dent->d_name; name2 = sub_dent->d_name; superblock_subpath = g_strconcat (name2, "/superblock", NULL); if (fstatat (sub_dfd_iter.fd, superblock_subpath, &stbuf, 0) < 0) { if (errno != ENOENT) { glnx_set_error_from_errno (error); return FALSE; } } else { g_autofree char *buf = g_strconcat (name1, name2, NULL); GString *out = g_string_new (""); char checksum[OSTREE_SHA256_STRING_LEN+1]; guchar csum[OSTREE_SHA256_DIGEST_LEN]; const char *dash = strchr (buf, '-'); ostree_checksum_b64_inplace_to_bytes (buf, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); if (dash) { g_string_append_c (out, '-'); ostree_checksum_b64_inplace_to_bytes (dash+1, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); } g_ptr_array_add (ret_deltas, g_string_free (out, FALSE)); } } } ot_transfer_out_value (out_deltas, &ret_deltas); return TRUE; }
void copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, gid_t gid, int flags) { char *p, lnk[MAXPATHLEN], copybuf[4096]; int len, homefd, srcfd, destfd; ssize_t sz; struct stat st; struct dirent *e; DIR *d; if (*dir == '/') dir++; if (mkdirat(rootfd, dir, mode) != 0 && errno != EEXIST) { warn("mkdir(%s)", dir); return; } fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW); if (flags > 0) chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW); if (skelfd == -1) return; homefd = openat(rootfd, dir, O_DIRECTORY); if ((d = fdopendir(skelfd)) == NULL) { close(skelfd); close(homefd); return; } while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; p = e->d_name; if (fstatat(skelfd, p, &st, AT_SYMLINK_NOFOLLOW) == -1) continue; if (strncmp(p, "dot.", 4) == 0) /* Conversion */ p += 3; if (S_ISDIR(st.st_mode)) { copymkdir(homefd, p, openat(skelfd, e->d_name, O_DIRECTORY), st.st_mode & _DEF_DIRMODE, uid, gid, st.st_flags); continue; } if (S_ISLNK(st.st_mode) && (len = readlinkat(skelfd, e->d_name, lnk, sizeof(lnk) -1)) != -1) { lnk[len] = '\0'; symlinkat(lnk, homefd, p); fchownat(homefd, p, uid, gid, AT_SYMLINK_NOFOLLOW); continue; } if (!S_ISREG(st.st_mode)) continue; if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1) continue; destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL, st.st_mode); if (destfd == -1) { close(srcfd); continue; } while ((sz = read(srcfd, copybuf, sizeof(copybuf))) > 0) write(destfd, copybuf, sz); close(srcfd); /* * Propagate special filesystem flags */ fchown(destfd, uid, gid); fchflags(destfd, st.st_flags); close(destfd); } closedir(d); }
int rpl_utimensat (int fd, char const *file, struct timespec const times[2], int flag) { # ifdef __linux__ struct timespec ts[2]; # endif /* See comments in utimens.c for details. */ static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no. */ if (0 <= utimensat_works_really) { int result; # ifdef __linux__ struct stat st; /* As recently as Linux kernel 2.6.32 (Dec 2009), several file systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, but work if both times are either explicitly specified or UTIME_NOW. Work around it with a preparatory [l]stat prior to calling utimensat; fortunately, there is not much timing impact due to the extra syscall even on file systems where UTIME_OMIT would have worked. FIXME: Simplify this in 2012, when file system bugs are no longer common. */ if (times && (times[0].tv_nsec == UTIME_OMIT || times[1].tv_nsec == UTIME_OMIT)) { if (fstatat (fd, file, &st, flag)) return -1; if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) return 0; if (times[0].tv_nsec == UTIME_OMIT) ts[0] = get_stat_atime (&st); else ts[0] = times[0]; if (times[1].tv_nsec == UTIME_OMIT) ts[1] = get_stat_mtime (&st); else ts[1] = times[1]; times = ts; } # ifdef __hppa__ /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec values. */ else if (times && ((times[0].tv_nsec != UTIME_NOW && (times[0].tv_nsec < 0 || times[0].tv_nsec >= 1000000000)) || (times[1].tv_nsec != UTIME_NOW && (times[1].tv_nsec < 0 || times[1].tv_nsec >= 1000000000)))) { errno = EINVAL; return -1; } # endif # endif /* __linux__ */ result = utimensat (fd, file, times, flag); /* Linux kernel 2.6.25 has a bug where it returns EINVAL for UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which local_utimensat works around. Meanwhile, EINVAL for a bad flag is indeterminate whether the native utimensat works, but local_utimensat will also reject it. */ if (result == -1 && errno == EINVAL && (flag & ~AT_SYMLINK_NOFOLLOW)) return result; if (result == 0 || (errno != ENOSYS && errno != EINVAL)) { utimensat_works_really = 1; return result; } } /* No point in trying openat/futimens, since on Linux, futimens is implemented with the same syscall as utimensat. Only avoid the native utimensat due to an ENOSYS failure; an EINVAL error was data-dependent, and the next caller may pass valid data. */ if (0 <= utimensat_works_really && errno == ENOSYS) utimensat_works_really = -1; return local_utimensat (fd, file, times, flag); }
int get_size(const char *pkgname, userid_t userid, const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath, int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int64_t* _asecsize) { DIR *d; int dfd; struct dirent *de; struct stat s; char path[PKG_PATH_MAX]; int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; int64_t asecsize = 0; /* count the source apk as code -- but only if it's not * on the /system partition and its not on the sdcard. */ if (validate_system_app_path(apkpath) && strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) { if (stat(apkpath, &s) == 0) { codesize += stat_size(&s); } } /* count the forward locked apk as code if it is given */ if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') { if (stat(fwdlock_apkpath, &s) == 0) { codesize += stat_size(&s); } } /* count the cached dexfile as code */ if (!create_cache_path(path, apkpath)) { if (stat(path, &s) == 0) { codesize += stat_size(&s); } } /* add in size of any libraries */ /* If the app is on the SD card, the libraries are stored in asecpath. * If we don't check that asecpath is defined, the libraries will be * counted twice. Once in asecsize and once in codesize. */ if ((asecpath == NULL || asecpath[0] == '!') && libdirpath != NULL && libdirpath[0] != '!') { d = opendir(libdirpath); if (d != NULL) { dfd = dirfd(d); codesize += calculate_dir_size(dfd); closedir(d); } } /* compute asec size if it is given */ if (asecpath != NULL && asecpath[0] != '!') { if (stat(asecpath, &s) == 0) { asecsize += stat_size(&s); } } if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, userid)) { goto done; } d = opendir(path); if (d == NULL) { goto done; } dfd = dirfd(d); /* most stuff in the pkgdir is data, except for the "cache" * directory and below, which is cache, and the "lib" directory * and below, which is code... */ while ((de = readdir(d))) { const char *name = de->d_name; if (de->d_type == DT_DIR) { int subfd; int64_t statsize = 0; int64_t dirsize = 0; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { statsize = stat_size(&s); } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (subfd >= 0) { dirsize = calculate_dir_size(subfd); } if(!strcmp(name,"lib")) { codesize += dirsize + statsize; } else if(!strcmp(name,"cache")) { cachesize += dirsize + statsize; } else { datasize += dirsize + statsize; } } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) { // This is the symbolic link to the application's library // code. We'll count this as code instead of data, since // it is not something that the app creates. if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { codesize += stat_size(&s); } } else { if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { datasize += stat_size(&s); } } } closedir(d); done: *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; *_asecsize = asecsize; return 0; }
fsal_status_t XFSFSAL_unlink(fsal_handle_t * p_parent_directory_handle, /* IN */ fsal_name_t * p_object_name, /* IN */ fsal_op_context_t * p_context, /* IN */ fsal_attrib_list_t * p_parent_directory_attributes /* [IN/OUT ] */ ) { fsal_status_t status; int rc, errsv; struct stat buffstat, buffstat_parent; int fd; /* sanity checks. */ if(!p_parent_directory_handle || !p_context || !p_object_name) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_unlink); /* build the FID path */ TakeTokenFSCall(); status = fsal_internal_handle2fd(p_context, p_parent_directory_handle, &fd, O_DIRECTORY); ReleaseTokenFSCall(); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_unlink); /* get directory metadata */ TakeTokenFSCall(); rc = fstat(fd, &buffstat_parent); errsv = errno; ReleaseTokenFSCall(); if(rc) { close(fd); if(errsv == ENOENT) Return(ERR_FSAL_STALE, errsv, INDEX_FSAL_unlink); else Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_unlink); } /* build the child path */ /* get file metadata */ TakeTokenFSCall(); rc = fstatat(fd, p_object_name->name, &buffstat, AT_SYMLINK_NOFOLLOW); errsv = errno; ReleaseTokenFSCall(); if(rc) { close(fd); Return(posix2fsal_error(errno), errno, INDEX_FSAL_unlink); } /* check access rights */ /* Sticky bit on the directory => the user who wants to delete the file must own it or its parent dir */ if((buffstat_parent.st_mode & S_ISVTX) && buffstat_parent.st_uid != ((xfsfsal_op_context_t *)p_context)->credential.user && buffstat.st_uid != ((xfsfsal_op_context_t *)p_context)->credential.user && ((xfsfsal_op_context_t *)p_context)->credential.user != 0) { close(fd); Return(ERR_FSAL_ACCESS, 0, INDEX_FSAL_unlink); } /* client must be able to lookup the parent directory and modify it */ status = fsal_internal_testAccess(p_context, FSAL_W_OK | FSAL_X_OK, &buffstat_parent, NULL); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_unlink); /****************************** * DELETE FROM THE FILESYSTEM * ******************************/ TakeTokenFSCall(); /* If the object to delete is a directory, use 'rmdir' to delete the object, else use 'unlink' */ rc = (S_ISDIR(buffstat.st_mode)) ? unlinkat(fd, p_object_name->name, AT_REMOVEDIR) : unlinkat(fd, p_object_name->name, 0); errsv = errno; ReleaseTokenFSCall(); close(fd); if(rc) Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_unlink); /*********************** * FILL THE ATTRIBUTES * ***********************/ if(p_parent_directory_attributes) { status = XFSFSAL_getattrs(p_parent_directory_handle, p_context, p_parent_directory_attributes); if(FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(p_parent_directory_attributes->asked_attributes); FSAL_SET_MASK(p_parent_directory_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR); } } /* OK */ Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_unlink); }
fsal_status_t XFSFSAL_readdir(fsal_dir_t * dir_descriptor, /* IN */ fsal_cookie_t startposition, /* IN */ fsal_attrib_mask_t get_attr_mask, /* IN */ fsal_mdsize_t buffersize, /* IN */ fsal_dirent_t * p_pdirent, /* OUT */ fsal_cookie_t * end_position, /* OUT */ fsal_count_t * p_nb_entries, /* OUT */ fsal_boolean_t * p_end_of_dir /* OUT */ ) { xfsfsal_dir_t * p_dir_descriptor = (xfsfsal_dir_t * ) dir_descriptor; xfsfsal_cookie_t start_position; xfsfsal_cookie_t * p_end_position = (xfsfsal_cookie_t *) end_position; fsal_status_t st; fsal_count_t max_dir_entries; char buff[BUF_SIZE]; struct linux_dirent *dp = NULL; int bpos = 0; int tmpfd = 0; char d_type; struct stat buffstat; int errsv = 0, rc = 0; memset(buff, 0, BUF_SIZE); /*****************/ /* sanity checks */ /*****************/ if(!p_dir_descriptor || !p_pdirent || !p_end_position || !p_nb_entries || !p_end_of_dir) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_readdir); max_dir_entries = (buffersize / sizeof(fsal_dirent_t)); /***************************/ /* seek into the directory */ /***************************/ start_position.data.cookie = (off_t) startposition.data; errno = 0; if(start_position.data.cookie == 0) { //rewinddir(p_dir_descriptor->p_dir); rc = errno; } else { //seekdir(p_dir_descriptor->p_dir, start_position.cookie); rc = errno; } if(rc) Return(posix2fsal_error(rc), rc, INDEX_FSAL_readdir); /************************/ /* browse the directory */ /************************/ *p_nb_entries = 0; while(*p_nb_entries < max_dir_entries) { /***********************/ /* read the next entry */ /***********************/ TakeTokenFSCall(); rc = syscall(SYS_getdents, p_dir_descriptor->fd, buff, BUF_SIZE); ReleaseTokenFSCall(); if(rc < 0) { rc = errno; Return(posix2fsal_error(rc), rc, INDEX_FSAL_readdir); } /* End of directory */ if(rc == 0) { *p_end_of_dir = 1; break; } /***********************************/ /* Get information about the entry */ /***********************************/ for(bpos = 0; bpos < rc;) { dp = (struct linux_dirent *)(buff + bpos); d_type = *(buff + bpos + dp->d_reclen - 1); /** @todo not used for the moment. Waiting for information on symlink management */ bpos += dp->d_reclen; /* LogFullDebug(COMPONENT_FSAL, "\tino=%8ld|%8lx off=%d|%x reclen=%d|%x name=%s|%d", dp->d_ino, dp->d_ino, (int)dp->d_off, (int)dp->d_off, dp->d_reclen, dp->d_reclen, dp->d_name, (int)dp->d_name[0] ) ; */ if(!(*p_nb_entries < max_dir_entries)) break; /* skip . and .. */ if(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; /* build the full path of the file into "fsalpath */ if(FSAL_IS_ERROR (st = FSAL_str2name(dp->d_name, FSAL_MAX_NAME_LEN, &(p_pdirent[*p_nb_entries].name)))) ReturnStatus(st, INDEX_FSAL_readdir); d_type = DT_UNKNOWN; if((tmpfd = openat(p_dir_descriptor->fd, dp->d_name, O_RDONLY | O_NOFOLLOW, 0600)) < 0) { if(errno != ELOOP) /* ( p_dir_descriptor->fd, dp->d_name) is not a symlink */ Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_readdir); else d_type = DT_LNK; } /* get object handle */ TakeTokenFSCall(); if(d_type != DT_LNK) { st = fsal_internal_fd2handle((fsal_op_context_t *)&(p_dir_descriptor->context), tmpfd, &(p_pdirent[*p_nb_entries].handle)); close(tmpfd); } else { if(fstatat(p_dir_descriptor->fd, dp->d_name, &buffstat, AT_SYMLINK_NOFOLLOW) < 0) { ReleaseTokenFSCall(); Return(posix2fsal_error(errno), errno, INDEX_FSAL_readdir); } st = fsal_internal_inum2handle(&p_dir_descriptor->context, buffstat.st_ino, &(p_pdirent[*p_nb_entries].handle)); if(FSAL_IS_ERROR(st)) { ReleaseTokenFSCall(); ReturnStatus(st, INDEX_FSAL_readdir); } p_pdirent[*p_nb_entries].attributes.asked_attributes = get_attr_mask; st = posix2fsal_attributes(&buffstat, &p_pdirent[*p_nb_entries].attributes); if(FSAL_IS_ERROR(st)) { ReleaseTokenFSCall(); FSAL_CLEAR_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes); FSAL_SET_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes, FSAL_ATTR_RDATTR_ERR); ReturnStatus(st, INDEX_FSAL_getattrs); } } ReleaseTokenFSCall(); if(FSAL_IS_ERROR(st)) ReturnStatus(st, INDEX_FSAL_readdir); /************************ * Fills the attributes * ************************/ if(d_type != DT_LNK) { p_pdirent[*p_nb_entries].attributes.asked_attributes = get_attr_mask; st = XFSFSAL_getattrs((xfsfsal_handle_t *) (&(p_pdirent[*p_nb_entries].handle)), (xfsfsal_op_context_t *) & p_dir_descriptor->context, &p_pdirent[*p_nb_entries].attributes); if(FSAL_IS_ERROR(st)) { FSAL_CLEAR_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes); FSAL_SET_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes, FSAL_ATTR_RDATTR_ERR); } } //p_pdirent[*p_nb_entries].cookie.cookie = dp->d_off; ((xfsfsal_cookie_t *) (&p_pdirent[*p_nb_entries].cookie))->data.cookie = dp->d_off; p_pdirent[*p_nb_entries].nextentry = NULL; if(*p_nb_entries) p_pdirent[*p_nb_entries - 1].nextentry = &(p_pdirent[*p_nb_entries]); //(*p_end_position) = p_pdirent[*p_nb_entries].cookie; memcpy((char *)p_end_position, (char *)&p_pdirent[*p_nb_entries].cookie, sizeof(xfsfsal_cookie_t)); (*p_nb_entries)++; } /* for */ } /* While */ Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_readdir); }
int pkg_sshserve(int fd) { struct stat st; char *line = NULL; char *file, *age; size_t linecap = 0, r; ssize_t linelen; time_t mtime = 0; const char *errstr; int ffd; char buf[BUFSIZ]; char fpath[MAXPATHLEN]; char rpath[MAXPATHLEN]; const char *restricted = NULL; restricted = pkg_object_string(pkg_config_get("SSH_RESTRICT_DIR")); printf("ok: pkg "PKGVERSION"\n"); for (;;) { if ((linelen = getline(&line, &linecap, stdin)) < 0) break; if (linelen == 0) continue; /* trim cr */ if (line[linelen - 1] == '\n') line[linelen - 1] = '\0'; if (strcmp(line, "quit") == 0) return (EPKG_OK); if (strncmp(line, "get ", 4) != 0) { printf("ko: unknown command '%s'\n", line); continue; } file = line + 4; if (*file == '/') file++; else if (*file == '\0') { printf("ko: bad command get, expecting 'get file age'\n"); continue; } pkg_debug(1, "SSH server> file requested: %s", file); age = file; while (!isspace(*age)) { if (*age == '\0') { age = NULL; break; } age++; } if (age == NULL) { printf("ko: bad command get, expecting 'get file age'\n"); continue; } *age = '\0'; age++; while (isspace(*age)) { if (*age == '\0') { age = NULL; break; } age++; } if (age == NULL) { printf("ko: bad command get, expecting 'get file age'\n"); continue; } mtime = strtonum(age, 0, LONG_MAX, &errstr); if (errstr) { printf("ko: bad number %s: %s\n", age, errstr); continue; } #ifdef HAVE_CAPSICUM if (!cap_sandboxed() && restricted != NULL) { #else if (restricted != NULL) { #endif if (chdir(restricted)) { printf("ko: chdir failed (%s)\n", restricted); continue; } if (realpath(file, fpath) == NULL || realpath(restricted, rpath) == NULL || strncmp(fpath, rpath, strlen(rpath)) != 0) { printf("ko: file not found\n"); continue; } } if (fstatat(fd, file, &st, 0) == -1) { pkg_debug(1, "SSH server> fstatat failed"); printf("ko: file not found\n"); continue; } if (!S_ISREG(st.st_mode)) { printf("ko: not a file\n"); continue; } if (st.st_mtime <= mtime) { printf("ok: 0\n"); continue; } if ((ffd = openat(fd, file, O_RDONLY)) == -1) { printf("ko: file not found\n"); continue; } printf("ok: %" PRIdMAX "\n", (intmax_t)st.st_size); pkg_debug(1, "SSH server> sending ok: %" PRIdMAX "", (intmax_t)st.st_size); while ((r = read(ffd, buf, sizeof(buf))) > 0) { pkg_debug(1, "SSH server> sending data"); fwrite(buf, 1, r, stdout); } pkg_debug(1, "SSH server> finished"); close(ffd); } free(line); return (EPKG_OK); }
static int convert_from_old(const char *pkg_add_dbdir, bool dry_run) { DIR *d; struct dirent *dp; struct pkg *p = NULL; char path[MAXPATHLEN]; struct pkgdb *db = NULL; struct stat sb; int lock_type = PKGDB_LOCK_EXCLUSIVE; int ret; if (dry_run) ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL); else ret = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE| PKGDB_MODE_CREATE, PKGDB_DB_LOCAL); if (ret == EPKG_ENOACCESS) { warnx("Insufficient privileges to convert packages"); return (EX_NOPERM); } else if (ret != EPKG_OK && ret != EPKG_ENODB) { warnx("Error accessing the package database"); return (EX_SOFTWARE); } if ((d = opendir(pkg_add_dbdir)) == NULL) err(EX_NOINPUT, "%s", pkg_add_dbdir); if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) { return (EX_IOERR); } if (dry_run) lock_type = PKGDB_LOCK_READONLY; if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) { pkgdb_close(db); warnx("Cannot get an advisory lock on a database, it is locked" " by another process"); return (EX_TEMPFAIL); } while ((dp = readdir(d)) != NULL) { if (fstatat(dirfd(d), dp->d_name, &sb, 0) == 0 && S_ISDIR(sb.st_mode)) { if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) continue; if (p != NULL) pkg_free(p); if (pkg_new(&p, PKG_OLD_FILE) != EPKG_OK) err(EX_OSERR, "malloc"); printf("Converting %s...\n", dp->d_name); snprintf(path, sizeof(path), "%s/%s", pkg_add_dbdir, dp->d_name); if (pkg_old_load_from_path(p, path) != EPKG_OK) { fprintf(stderr, "Skipping invalid package: %s\n", path); continue; } pkg_from_old(p); if (!dry_run) pkgdb_register_ports(db, p); } } pkg_free(p); pkgdb_release_lock(db, lock_type); pkgdb_close(db); return (EX_OK); }
static gboolean export_dir (int source_parent_fd, const char *source_name, const char *source_relpath, int destination_parent_fd, const char *destination_name, const char *required_prefix, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int res; g_auto(GLnxDirFdIterator) source_iter = {0}; glnx_fd_close int destination_dfd = -1; struct dirent *dent; if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error)) goto out; do res = mkdirat (destination_parent_fd, destination_name, 0777); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != EEXIST) { glnx_set_error_from_errno (error); goto out; } } if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name, &destination_dfd, cancellable, error)) goto out; while (TRUE) { struct stat stbuf; g_autofree char *source_printable = NULL; if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) continue; else { glnx_set_error_from_errno (error); goto out; } } if (S_ISDIR (stbuf.st_mode)) { g_autofree gchar *child_relpath = g_build_filename(source_relpath, dent->d_name, NULL); if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name, required_prefix, cancellable, error)) goto out; } else if (S_ISREG (stbuf.st_mode)) { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); if (!xdg_app_has_name_prefix (dent->d_name, required_prefix)) { g_print ("Not exporting %s, wrong prefix\n", source_printable); continue; } g_print ("Exporting %s\n", source_printable); if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf, destination_dfd, dent->d_name, GLNX_FILE_COPY_NOXATTRS, cancellable, error)) goto out; } else { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); g_print ("Not exporting non-regular file %s\n", source_printable); } } ret = TRUE; out: return ret; }
/** * Check if the list of links to a file is complete. * This function goes through all the names in the list of names an * checks if the name references the file given by the dev/inode pair. * It returns zero if all links are found and a negative error code * in case of errors. * * @param dev The device where the file resides. * @param ino The inode number of the file. * @param namearr a list of path names relative to the root of the given * device. These name are searched for links the file with the given * inode number. * @return Zero if all links to the file could be found. A negative * error code if something went wrong. In particular: * EXDEV: The given device is not mounted. * ENOENT: None of the file names references the given inode. * EBUSY: Some but not all links to the file were found in the * list of file names. * ENOMEM: Memory allocation failure. * EEXIST: At least one of the target files exists and would have to * be overwritten. Only returned if checkoverwrite is true. * EMLINK: The target file has more than one hardlink and this * hardlink will be broken by the commit. Only returned if * checkoverwrite is true. */ int pgfile_check(uint64_t dev, uint64_t ino, const char *namearr[], int checkoverwrite) { struct pgmount *mnt = pgmounts_find(dev); int totallinks = 0, foundlinks = 0; int i; struct pgfile_list list; struct stat statbuf; /* Device is currently not mounted. */ if (!mnt) return -EXDEV; if (pgfile_normalize_file_list(&list, namearr) < 0) return -ENOMEM; for (i=0; i<list.listlen; ++i) { int nlinks, off; char *targetname, *last, ch; nlinks = pgfile_validate_one_name(dev, ino, mnt->dirfd, list.list[i]); if (nlinks == 0) continue; totallinks = nlinks; foundlinks++; if (!checkoverwrite) continue; off = 0; while(list.list[i][off] == '/') off++; targetname = strdup(list.list[i]+off); if (targetname == NULL) { pgfile_free_list(&list); return -ENOMEM; } last = strrchr(targetname, '/'); if (last) last++; else last = targetname; if (sscanf(last, ".plgr.%*x%c%n", &ch, &off) < 1) { free(targetname); continue; } memmove(last, last+off, strlen(last+off)+1); nlinks = fstatat(mnt->dirfd, targetname, &statbuf, AT_SYMLINK_NOFOLLOW); free(targetname); if (nlinks < 0) continue; /* The target file exists. This is alway an error. */ pgfile_free_list(&list); if (S_ISDIR(statbuf.st_mode) || statbuf.st_nlink == 1) return -EEXIST; else return -EMLINK; } pgfile_free_list(&list); if (totallinks == 0) return -ENOENT; if (totallinks > foundlinks) return -EBUSY; return 0; }
int main(void) { /* dummy variables to prevent null warnings */ char path[] = ""; char mode[] = ""; char buf[1]; struct stat st; struct statfs stfs; struct statvfs stvfs; fpos_t fpos; off_t off; struct rlimit rlim; glob_t glb; int result; DIR* dir; struct dirent direntry; struct dirent* pdirentry; struct dirent** ppdirentry; const struct dirent *pconstdirentry; dir = 0; result = alphasort(&pconstdirentry, &pconstdirentry); creat(path, 0); fgetpos(0, &fpos); fopen(path, mode); freopen(path, mode, 0); fseeko(0, 0, 0); fsetpos(0, &fpos); fstat(0, &st); fstatat(0, path, &st, 0); fstatfs(0, &stfs); fstatvfs(0, &stvfs); ftello(0); ftruncate(0, 0); ftw(path, ftw_stub, 0); getdirentries(0, buf, 0, &off); getrlimit(0, &rlim); glob(path, 0, glob_stub, &glb); lockf(0, 0, 0); lseek(0, 0, 0); lstat(path, &st); mkostemp(path, 0); mkstemp(path); mmap(buf, 0, 0, 0, 0, 0); nftw(path, nftw_stub, 0, 0); open(path, 0); open(path, 0, 0); openat(0, path, 0); openat(0, path, 0, 0); posix_fadvise(0, 0, 0, 0); posix_fallocate(0, 0, 0); pread(0, buf, 0, 0); pwrite(0, buf, 0, 0); readdir(dir); readdir_r(dir, &direntry, &pdirentry); scandir(path, &ppdirentry, scandir_filter_stub, scandir_compare_stub); sendfile(0, 0, &off, 0); setrlimit(0, &rlim); stat(path, &st); statfs(path, &stfs); statvfs(path, &stvfs); tmpfile(); truncate(path, 0); result = versionsort(&pconstdirentry, &pconstdirentry); return result; }
bool sc_cgroup_freezer_occupied(const char *snap_name) { // Format the name of the cgroup hierarchy. char buf[PATH_MAX] = { 0 }; sc_must_snprintf(buf, sizeof buf, "snap.%s", snap_name); // Open the freezer cgroup directory. int cgroup_fd SC_CLEANUP(sc_cleanup_close) = -1; cgroup_fd = open(freezer_cgroup_dir, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); if (cgroup_fd < 0) { die("cannot open freezer cgroup (%s)", freezer_cgroup_dir); } // Open the proc directory. int proc_fd SC_CLEANUP(sc_cleanup_close) = -1; proc_fd = open("/proc", O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); if (proc_fd < 0) { die("cannot open /proc"); } // Open the hierarchy directory for the given snap. int hierarchy_fd SC_CLEANUP(sc_cleanup_close) = -1; hierarchy_fd = openat(cgroup_fd, buf, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); if (hierarchy_fd < 0) { if (errno == ENOENT) { return false; } die("cannot open freezer cgroup hierarchy for snap %s", snap_name); } // Open the "cgroup.procs" file. Alternatively we could open the "tasks" // file and see per-thread data but we don't need that. int cgroup_procs_fd SC_CLEANUP(sc_cleanup_close) = -1; cgroup_procs_fd = openat(hierarchy_fd, "cgroup.procs", O_RDONLY | O_NOFOLLOW | O_CLOEXEC); if (cgroup_procs_fd < 0) { die("cannot open cgroup.procs file for freezer cgroup hierarchy for snap %s", snap_name); } FILE *cgroup_procs SC_CLEANUP(sc_cleanup_file) = NULL; cgroup_procs = fdopen(cgroup_procs_fd, "r"); if (cgroup_procs == NULL) { die("cannot convert tasks file descriptor to FILE"); } cgroup_procs_fd = -1; // cgroup_procs_fd will now be closed by fclose. char *line_buf SC_CLEANUP(sc_cleanup_string) = NULL; size_t line_buf_size = 0; ssize_t num_read; struct stat statbuf; do { num_read = getline(&line_buf, &line_buf_size, cgroup_procs); if (num_read < 0 && errno != 0) { die("cannot read next PID belonging to snap %s", snap_name); } if (num_read <= 0) { break; } else { if (line_buf[num_read - 1] == '\n') { line_buf[num_read - 1] = '\0'; } else { die("could not find newline in cgroup.procs"); } } debug("found process id: %s\n", line_buf); if (fstatat(proc_fd, line_buf, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) { // The process may have died already. if (errno != ENOENT) { die("cannot stat /proc/%s", line_buf); } } debug("found process %s belonging to user %d", line_buf, statbuf.st_uid); return true; } while (num_read > 0); return false; }