// 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; // 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; } try { ctx->dir_paths->push( joined_path ); } catch( bad_alloc& ba ) { // OOM free( joined_path ); return -ENOMEM; } try { ctx->dir_queue->push( fd ); } catch( bad_alloc& ba ) { // OOM close( fd ); return -ENOMEM; } } // construct an inode for this entry child = VDEV_CALLOC( struct fskit_entry, 1 ); if( child == NULL ) { return -ENOMEM; } // regular file? if( S_ISREG( sb.st_mode ) ) { method_name = "fskit_entry_init_file"; rc = fskit_entry_init_file( child, sb.st_ino, dent->d_name, sb.st_uid, sb.st_gid, sb.st_mode & 0777 ); } // directory? else if( S_ISDIR( sb.st_mode ) ) { method_name = "fskit_entry_init_dir"; rc = fskit_entry_init_dir( child, ctx->parent_dir, sb.st_ino, dent->d_name, sb.st_uid, sb.st_gid, sb.st_mode & 0777 ); } // named pipe? else if( S_ISFIFO( sb.st_mode ) ) { method_name = "fskit_entry_init_fifo"; rc = fskit_entry_init_fifo( child, sb.st_ino, dent->d_name, sb.st_uid, sb.st_gid, sb.st_mode & 0777 ); } // unix domain socket? else if( S_ISSOCK( sb.st_mode ) ) { method_name = "fskit_entry_init_sock"; rc = fskit_entry_init_sock( child, sb.st_ino, dent->d_name, sb.st_uid, sb.st_gid, sb.st_mode & 0777 ); } // character device? else if( S_ISCHR( sb.st_mode ) ) { method_name = "fskit_entry_init_chr"; rc = fskit_entry_init_chr( child, sb.st_ino, dent->d_name, sb.st_uid, sb.st_gid, sb.st_mode, sb.st_rdev ); } // block device? else if( S_ISBLK( sb.st_mode ) ) { method_name = "fskit_entry_init_blk"; rc = fskit_entry_init_blk( child, sb.st_ino, dent->d_name, sb.st_uid, sb.st_gid, sb.st_mode, sb.st_rdev ); } // symbolic link? else if( S_ISLNK( sb.st_mode ) ) { // read the link first... memset( linkbuf, 0, 8193 ); rc = readlinkat( dirfd, dent->d_name, linkbuf, 8192 ); if( rc < 0 ) { rc = -errno; // mask error, but log serious ones. this link will not appear in the listing if( rc != -ENOENT && rc != -EACCES ) { vdev_error("readlinkat(%d, '%s') rc = %d\n", dirfd, dent->d_name, rc ); } free( child ); child = NULL; return 0; } method_name = "fskit_entry_init_symlink"; rc = fskit_entry_init_symlink( child, sb.st_ino, dent->d_name, linkbuf ); } // success? if( rc != 0 ) { vdev_error("%s( on %d, '%s' ) rc = %d\n", method_name, dirfd, dent->d_name, rc ); free( child ); child = NULL; return rc; } // insert into parent rc = fskit_entry_attach_lowlevel( ctx->parent_dir, child ); if( rc != 0 ) { // OOM fskit_entry_destroy( ctx->core, child, false ); free( child ); child = NULL; return rc; } // success! return rc; }
// low-level mkdir: create a child and insert it into the parent. // parent must be a directory // parent must be write-locked // return -EEXIST if an entry with the given name (path_basename) already exists in parent. // return -EIO if we couldn't allocate an inode // return -ENOMEM if we couldn't allocate memory static int fskit_mkdir_lowlevel( struct fskit_core* core, char const* path, struct fskit_entry* parent, char const* path_basename, mode_t mode, uint64_t user, uint64_t group, void* cls ) { // resolve the child within the parent struct fskit_entry* child = fskit_entry_set_find_name( parent->children, path_basename ); int err = 0; void* app_dir_data = NULL; if( child != NULL ) { fskit_entry_wlock( child ); err = fskit_entry_try_garbage_collect( core, path, parent, child ); if( err >= 0 ) { if( err == 0 ) { // detached but not destroyed fskit_entry_unlock( child ); } // name is free child = NULL; } else { fskit_entry_unlock( child ); if( err == -EEXIST ) { // still exists; can't be garbage-collected return -EEXIST; } else { // shouldn't happen fskit_error("BUG: fskit_entry_try_garbage_collect(%s) rc = %d\n", path, err ); return -EIO; } } } if( child == NULL ) { // create an fskit_entry and attach it child = CALLOC_LIST( struct fskit_entry, 1 ); if( child == NULL ) { return -ENOMEM; } // get an inode for this child uint64_t child_inode = fskit_core_inode_alloc( core, parent, child ); if( child_inode == 0 ) { // error in allocation fskit_error("fskit_core_inode_alloc(%s) failed\n", path ); fskit_safe_free( child ); return -EIO; } // set up the directory err = fskit_entry_init_dir( child, parent, child_inode, user, group, mode ); if( err != 0 ) { fskit_error("fskit_entry_init_dir(%s) rc = %d\n", path, err ); fskit_safe_free( child ); return err; } // reference this directory, so it won't disappear during the user's route child->open_count++; // almost done. run the route callback for this path if needed err = fskit_run_user_mkdir( core, path, parent, child, mode, cls, &app_dir_data ); child->open_count--; if( err != 0 ) { // user route failed fskit_error("fskit_run_user_mkdir(%s) rc = %d\n", path, err ); fskit_entry_destroy( core, child, false ); fskit_safe_free( child ); } else { fskit_entry_set_user_data( child, app_dir_data ); // attach to parent fskit_entry_attach_lowlevel( parent, child, path_basename ); } }