static int scrub_write_progress(pthread_mutex_t *m, const char *fsid, struct scrub_progress *data, int n) { int ret; int err; int fd = -1; int old; ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old); if (ret) { err = -ret; goto out3; } ret = pthread_mutex_lock(m); if (ret) { err = -ret; goto out2; } fd = scrub_open_file_w(SCRUB_DATA_FILE, fsid, "tmp"); if (fd < 0) { err = fd; goto out1; } err = scrub_write_file(fd, fsid, data, n); if (err) goto out1; err = scrub_rename_file(SCRUB_DATA_FILE, fsid, "tmp"); if (err) goto out1; out1: if (fd >= 0) { ret = close(fd); if (ret) err = -errno; } ret = pthread_mutex_unlock(m); if (ret && !err) err = -ret; out2: ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old); if (ret && !err) err = -ret; out3: return err; }
/* nb: returns a negative errno via ERR_PTR */ static void *scrub_progress_cycle(void *ctx) { int ret; int perr = 0; /* positive / pthread error returns */ int old; int i; char fsid[37]; struct scrub_progress *sp; struct scrub_progress *sp_last; struct scrub_progress *sp_shared; struct timeval tv; struct scrub_progress_cycle *spc = ctx; int ndev = spc->fi->num_devices; int this = 1; int last = 0; int peer_fd = -1; struct pollfd accept_poll_fd = { .fd = spc->prg_fd, .events = POLLIN, .revents = 0, }; struct pollfd write_poll_fd = { .events = POLLOUT, .revents = 0, }; struct sockaddr_un peer; socklen_t peer_size = sizeof(peer); perr = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); if (perr) goto out; uuid_unparse(spc->fi->fsid, fsid); for (i = 0; i < ndev; ++i) { sp = &spc->progress[i]; sp_last = &spc->progress[i + ndev]; sp_shared = &spc->shared_progress[i]; sp->scrub_args.devid = sp_last->scrub_args.devid = sp_shared->scrub_args.devid; sp->fd = sp_last->fd = spc->fdmnt; sp->stats.t_start = sp_last->stats.t_start = sp_shared->stats.t_start; sp->resumed = sp_last->resumed = sp_shared->resumed; sp->skip = sp_last->skip = sp_shared->skip; sp->stats.finished = sp_last->stats.finished = sp_shared->stats.finished; } while (1) { ret = poll(&accept_poll_fd, 1, 5 * 1000); if (ret == -1) { ret = -errno; goto out; } if (ret) peer_fd = accept(spc->prg_fd, (struct sockaddr *)&peer, &peer_size); gettimeofday(&tv, NULL); this = (this + 1)%2; last = (last + 1)%2; for (i = 0; i < ndev; ++i) { sp = &spc->progress[this * ndev + i]; sp_last = &spc->progress[last * ndev + i]; sp_shared = &spc->shared_progress[i]; if (sp->stats.finished) continue; progress_one_dev(sp); sp->stats.duration = tv.tv_sec - sp->stats.t_start; if (!sp->ret) continue; if (sp->ioctl_errno != ENOTCONN && sp->ioctl_errno != ENODEV) { ret = -sp->ioctl_errno; goto out; } /* * scrub finished or device removed, check the * finished flag. if unset, just use the last * result we got for the current write and go * on. flag should be set on next cycle, then. */ perr = pthread_mutex_lock(&sp_shared->progress_mutex); if (perr) goto out; if (!sp_shared->stats.finished) { perr = pthread_mutex_unlock( &sp_shared->progress_mutex); if (perr) goto out; memcpy(sp, sp_last, sizeof(*sp)); continue; } perr = pthread_mutex_unlock(&sp_shared->progress_mutex); if (perr) goto out; memcpy(sp, sp_shared, sizeof(*sp)); memcpy(sp_last, sp_shared, sizeof(*sp)); } if (peer_fd != -1) { write_poll_fd.fd = peer_fd; ret = poll(&write_poll_fd, 1, 0); if (ret == -1) { ret = -errno; goto out; } if (ret) { ret = scrub_write_file( peer_fd, fsid, &spc->progress[this * ndev], ndev); if (ret) goto out; } close(peer_fd); peer_fd = -1; } if (!spc->do_record) continue; ret = scrub_write_progress(spc->write_mutex, fsid, &spc->progress[this * ndev], ndev); if (ret) goto out; } out: if (peer_fd != -1) close(peer_fd); if (perr) ret = -perr; return ERR_PTR(ret); } static struct scrub_file_record *last_dev_scrub( struct scrub_file_record *const *const past_scrubs, u64 devid) { int i; if (!past_scrubs || IS_ERR(past_scrubs)) return NULL; for (i = 0; past_scrubs[i]; ++i) if (past_scrubs[i]->devid == devid) return past_scrubs[i]; return NULL; } int mkdir_p(char *path) { int i; int ret; for (i = 1; i < strlen(path); ++i) { if (path[i] != '/') continue; path[i] = '\0'; ret = mkdir(path, 0777); if (ret && errno != EEXIST) return 1; path[i] = '/'; } return 0; }