+ return ret;
+}
+
+void lock_file(struct thread_data *td, struct fio_file *f)
+{
+ if (f && f->sem) {
+ if (f->sem_owner == td && f->sem_batch--)
+ return;
+
+ fio_sem_down(f->sem);
+ f->sem_owner = td;
+ f->sem_batch = td->o.lockfile_batch;
+ }
+}
+
+void unlock_file(struct fio_file *f)
+{
+ if (f && f->sem) {
+ int sem_val;
+
+ if (f->sem_batch)
+ return;
+
+ sem_getvalue(&f->sem->sem, &sem_val);
+ if (!sem_val)
+ f->sem_owner = NULL;
+ fio_sem_up(f->sem);
+ }
+}
+
+static int recurse_dir(struct thread_data *td, const char *dirname)
+{
+ struct dirent *dir;
+ int ret = 0;
+ DIR *D;
+
+ D = opendir(dirname);
+ if (!D) {
+ char buf[FIO_VERROR_SIZE];
+
+ snprintf(buf, FIO_VERROR_SIZE - 1, "opendir(%s)", dirname);
+ td_verror(td, errno, buf);
+ return 1;
+ }
+
+ while ((dir = readdir(D)) != NULL) {
+ char full_path[PATH_MAX];
+ struct stat sb;
+
+ if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
+ continue;
+
+ sprintf(full_path, "%s/%s", dirname, dir->d_name);
+
+ if (lstat(full_path, &sb) == -1) {
+ if (errno != ENOENT) {
+ td_verror(td, errno, "stat");
+ return 1;
+ }
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ add_file(td, full_path);
+ td->o.nr_files++;
+ continue;
+ }
+ if (!S_ISDIR(sb.st_mode))
+ continue;
+
+ if ((ret = recurse_dir(td, full_path)) != 0)
+ break;
+ }
+
+ closedir(D);
+ return ret;
+}
+
+int add_dir_files(struct thread_data *td, const char *path)
+{
+ int ret = recurse_dir(td, path);
+
+ if (!ret)
+ log_info("fio: opendir added %d files\n", td->o.nr_files);
+
+ return ret;
+}
+
+void dup_files(struct thread_data *td, struct thread_data *org)
+{
+ struct fio_file *f;
+ unsigned int i;
+ size_t bytes;
+
+ if (!org->files)
+ return;
+
+ bytes = org->files_index * sizeof(f);
+ td->files = malloc(bytes);
+ memcpy(td->files, org->files, bytes);
+
+ for_each_file(td, f, i) {
+ struct fio_file *__f;
+
+ __f = smalloc(sizeof(*__f));
+
+ if (f->file_name)
+ __f->file_name = smalloc_strdup(f->file_name);
+
+ td->files[i] = __f;
+ }
+}
+
+/*
+ * Returns the index that matches the filename, or -1 if not there
+ */
+int get_fileno(struct thread_data *td, const char *fname)
+{
+ struct fio_file *f;
+ unsigned int i;
+
+ for_each_file(td, f, i)
+ if (!strcmp(f->file_name, fname))
+ return i;
+
+ return -1;
+}
+
+/*
+ * For log usage, where we add/open/close files automatically
+ */
+void free_release_files(struct thread_data *td)
+{
+ close_files(td);
+ td->files_index = 0;
+ td->nr_normal_files = 0;