コード例 #1
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// read: read as usual, but from the underlying filesystem 
// NOTE: since this is backed by FUSE, this handler will only be called for regular files 
int vdevfs_read( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, char* buf, size_t len, off_t offset, void* handle_cls ) {
   
   // careful...
   int fd = 0;
   memcpy( &fd, &handle_cls, 4 );
   
   int rc = 0;
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   
   rc = lseek( fd, offset, SEEK_SET );
   if( rc < 0 ) {
      
      rc = -errno;
      vdev_error("lseek(%d '%s') rc = %d\n", fd, grp->path, rc );
      
      return rc;
   }
   
   rc = read( fd, buf, len );
   if( rc < 0 ) {
      
      rc = -errno;
      vdev_error("read(%d '%s') rc = %d\n", fd, grp->path, rc );
      
      return rc;
   }
   
   return rc;
}
コード例 #2
0
/*
 * Check that a file is valid.  All we can do in this case is check that it's
 * not in use by another pool.
 */
int
check_file(const char *file, boolean_t force, boolean_t isspare)
{
	char  *name;
	int fd;
	int ret = 0;
	pool_state_t state;
	boolean_t inuse;

	if ((fd = open(file, O_RDONLY)) < 0)
		return (0);

	if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) == 0 && inuse) {
		const char *desc;

		switch (state) {
		case POOL_STATE_ACTIVE:
			desc = gettext("active");
			break;

		case POOL_STATE_EXPORTED:
			desc = gettext("exported");
			break;

		case POOL_STATE_POTENTIALLY_ACTIVE:
			desc = gettext("potentially active");
			break;

		default:
			desc = gettext("unknown");
			break;
		}

		/*
		 * Allow hot spares to be shared between pools.
		 */
		if (state == POOL_STATE_SPARE && isspare)
			return (0);

		if (state == POOL_STATE_ACTIVE ||
		    state == POOL_STATE_SPARE || !force) {
			switch (state) {
			case POOL_STATE_SPARE:
				vdev_error(gettext("%s is reserved as a hot "
				    "spare for pool %s\n"), file, name);
				break;
			default:
				vdev_error(gettext("%s is part of %s pool "
				    "'%s'\n"), file, desc, name);
				break;
			}
			ret = -1;
		}

		free(name);
	}

	(void) close(fd);
	return (ret);
}
コード例 #3
0
ファイル: linux.c プロジェクト: 8l/vdev
// read the device major and minor number, using the devpath 
// return 0 on success, and set *major and *minor 
// return -ENOMEM on OOM 
// return -errno on failure to open or read
static int vdev_linux_sysfs_read_dev_nums( struct vdev_linux_context* ctx, char const* devpath, unsigned int* major, unsigned int* minor ) {
   
   int rc = 0;
   int fd = 0;
   ssize_t nr = 0;
   char devbuf[101];
   
   memset( devbuf, 0, 101 );
   
   char* full_devpath = vdev_linux_sysfs_fullpath( ctx->sysfs_mountpoint, devpath, "dev" );
   if( full_devpath == NULL ) {
      return -ENOMEM;
   }
   
   // open device path 
   fd = open( full_devpath, O_RDONLY );
   if( fd < 0 ) {
      
      rc = -errno;
      
      if( rc != -ENOENT ) {
         vdev_error("open('%s') rc = %d\n", full_devpath, rc );
      }
      
      free( full_devpath );
      return rc;
   }
   
   nr = vdev_read_uninterrupted( fd, devbuf, 100 );
   if( nr < 0 ) {
      
      rc = nr;
      vdev_error("read('%s') rc = %d\n", full_devpath, rc );
      
      free( full_devpath );
      close( fd );
      return rc;
   }
   
   close( fd );
   free( full_devpath );
   
   rc = vdev_linux_sysfs_parse_device_nums( devbuf, major, minor );
   
   if( rc != 0 ) {
      
      vdev_error("Failed to parse '%s'\n", devbuf );
      rc = -EIO;
   }
   
   return rc;
}
コード例 #4
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// for creating, opening, or stating files, verify that the caller is permitted according to our ACLs 
// return 0 on success 
// return -EPERM if denied 
// return other -errno on error 
static int vdevfs_access_check( struct vdevfs* vdev, struct fskit_fuse_state* fs_state, char const* method_name, char const* path ) {
   
   int rc = 0;
   pid_t pid = 0;
   uid_t uid = 0;
   gid_t gid = 0;
   struct stat sb;
   struct pstat ps;
   
   memset( &sb, 0, sizeof(struct stat) );
   sb.st_mode = 0777;
   
   memset( &ps, 0, sizeof(struct pstat) );
   
   // stat the calling process
   pid = fskit_fuse_get_pid();
   uid = fskit_fuse_get_uid( fs_state );
   gid = fskit_fuse_get_gid( fs_state );
   
   vdev_debug("%s('%s') from user %d group %d task %d\n", method_name, path, uid, gid, pid );
   
   // see who's asking 
   rc = pstat( pid, &ps, 0 );
   if( rc != 0 ) {
      
      vdev_error("pstat(%d) rc = %d\n", pid, rc );
      return -EIO;
   }
   
   // apply the ACLs on the stat buffer
   rc = vdev_acl_apply_all( vdev->config, vdev->acls, vdev->num_acls, path, &ps, uid, gid, &sb );
   if( rc < 0 ) {
      
      vdev_error("vdev_acl_apply_all(%s, uid=%d, gid=%d, pid=%d) rc = %d\n", path, uid, gid, pid, rc );
      return -EIO;
   }
   
   // omit entirely?
   if( rc == 0 || (sb.st_mode & 0777) == 0 ) {
      
      // filter
      vdev_debug("DENY '%s'\n", path );
      return -EPERM;
   }
   else {
      
      // accept!
      return 0;
   }
}
コード例 #5
0
ファイル: match.c プロジェクト: bjb/vdev
// parse a regex and append it to a list of regexes and strings
// return 0 on success
// return -EINVAL if the regex is invalid
// return -ENOMEM if we're out of memory
int vdev_match_regex_append( char*** strings, regex_t** regexes, size_t* len, char const* next ) {
   
   // verify that this is a valid regex 
   regex_t reg;
   int rc = 0;
   
   memset( &reg, 0, sizeof(regex_t) );
   
   rc = regcomp( &reg, next, REG_EXTENDED | REG_NEWLINE | REG_NOSUB );
   if( rc != 0 ) {
      
      vdev_error("regcomp(%s) rc = %d\n", next, rc );
      return -EINVAL;
   }
   
   char** new_strings = (char**)realloc( *strings, sizeof(char**) * (*len + 2) );
   regex_t* new_regexes = (regex_t*)realloc( *regexes, sizeof(regex_t) * (*len + 1) );
   
   if( new_strings == NULL || new_regexes == NULL ) {
      return -ENOMEM;
   }
   
   new_strings[*len] = vdev_strdup_or_null( next );
   new_strings[*len + 1] = NULL;
   
   new_regexes[*len] = reg;
   
   *strings = new_strings;
   *regexes = new_regexes;
   
   *len = *len + 1;
   
   return 0;
}
コード例 #6
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// close: close as usual
// NOTE: since this is backed by FUSE, this handler will only be called for regular files
int vdevfs_close( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, void* handle_cls ) {
   
   // only care about close() for files 
   if( fskit_entry_get_type( fent ) == FSKIT_ENTRY_TYPE_DIR ) {
      
      // done!
      return 0;
   }
   
   // careful...
   int fd = 0;
   memcpy( &fd, &handle_cls, 4 );
   
   int rc = 0;
   
   rc = close( fd );
   if( rc < 0 ) {
      
      rc = -errno;
      vdev_error("close(%d '%s') rc = %d\n", fd, grp->path, rc );
      
      return rc;
   }
   
   return rc;
}
コード例 #7
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// unlink/rmdir: remove the file or device node from the underlying filesystem 
int vdevfs_detach( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, void* inode_cls ) {
   
   int rc = 0;
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   struct fskit_fuse_state* fs_state = fskit_fuse_get_state();
   char const* method = NULL;
   
   if( fskit_entry_get_type( fent ) == FSKIT_ENTRY_TYPE_DIR ) {
      
      method = "rmdir";
   }
   else {
      
      method = "unlink";
   }
   
   
   rc = vdevfs_access_check( vdev, fs_state, method, grp->path );
   if( rc < 0 ) {
      
      // denied!
      return -ENOENT;
   }
   
   if( rc != 0 ) {
      
      rc = -errno;
      vdev_error("%s('%s', '%s') rc = %d\n", method, vdev->mountpoint, grp->path, rc );
      
      return rc;
   }
   
   return 0;
}
コード例 #8
0
ファイル: linux.c プロジェクト: 8l/vdev
// read the kernel-given device subsystem from sysfs 
// return 0 on success, and set *subsystem
// return -ENOMEM on OOM 
// return negative on readlink failure
static int vdev_linux_sysfs_read_subsystem( struct vdev_linux_context* ctx, char const* devpath, char** subsystem ) {
   
   int rc = 0;
   char linkpath[PATH_MAX+1];
   size_t linkpath_len = PATH_MAX;
   char* subsystem_path = NULL;
   
   memset( linkpath, 0, PATH_MAX+1 );
   
   subsystem_path = vdev_linux_sysfs_fullpath( ctx->sysfs_mountpoint, devpath, "subsystem" );
   if( subsystem_path == NULL ) {
      return -ENOMEM;
   }
   
   rc = readlink( subsystem_path, linkpath, linkpath_len );
   if( rc < 0 ) {
      
      rc = -errno;
      vdev_error("readlink('%s') rc = %d\n", subsystem_path, rc );
      free( subsystem_path );
      return rc;
   }
   
   free( subsystem_path );
   
   *subsystem = vdev_basename( linkpath, NULL );
   if( *subsystem == NULL ) {
      
      return -ENOMEM;
   }
   
   return 0;
}
コード例 #9
0
/*
 * Validate a device, passing the bulk of the work off to libdiskmgt.
 */
int
check_slice(const char *path, int force, boolean_t wholedisk, boolean_t isspare)
{
	char *msg;
	int error = 0;
	int ret = 0;

	if (dm_inuse((char *)path, &msg, isspare ? DM_WHO_ZPOOL_SPARE :
	    (force ? DM_WHO_ZPOOL_FORCE : DM_WHO_ZPOOL), &error) || error) {
		if (error != 0) {
			libdiskmgt_error(error);
			return (0);
		} else {
			vdev_error("%s", msg);
			free(msg);
			ret = -1;
		}

	}

	/*
	 * If we're given a whole disk, ignore overlapping slices since we're
	 * about to label it anyway.
	 */
	error = 0;
	if (!wholedisk && !force &&
	    (dm_isoverlapping((char *)path, &msg, &error) || error)) {
		if (error != 0) {
			libdiskmgt_error(error);
			return (0);
		} else {
			vdev_error("%s overlaps with %s\n", path, msg);
			free(msg);
		}

		ret = -1;
	}

	return (ret);
}
コード例 #10
0
ファイル: linux.c プロジェクト: 8l/vdev
// find sysfs mountpoint in /proc/mounts
// this is apparently superfluous (it *should* be mounted at /sys), but you never know.
// mountpoint must be big enough (PATH_MAX will do)
// return 0 on success
// return -ENOSYS if sysfs is not mounted
// return -ENOMEM if the buffer isn't big enough 
// return -EINVAL if somehow we failed to parse a mount entry
// return negative for some other errors (like access permission failures, or /proc not mounted)
static int vdev_linux_find_sysfs_mountpoint( char* mountpoint, size_t mountpoint_len ) {
   
   FILE* f = NULL;
   int rc = 0;
   
   char mntbuf[4096];
   struct mntent ment_buf;
   struct mntent* ment_ptr = NULL;
   int ent_count = 1;
   bool found = false;
   
   f = fopen( "/proc/mounts", "r" );
   if( f == NULL ) {
      
      rc = -errno;
      fprintf(stderr, "Failed to open /proc/mounts, rc = %d\n", rc );
      return rc;
   }
   
   // scan for sysfs mount type
   while( 1 ) {
      
      ment_ptr = getmntent_r( f, &ment_buf, mntbuf, 4096 );
      if( ment_ptr == NULL ) {
         
         vdev_error("Failed on processing entry #%d of /proc/mounts\n", ent_count );
         rc = -EINVAL;
         break;
      }
      
      if( strcmp( ment_ptr->mnt_type, "sysfs" ) == 0 ) {
         
         // found!
         strncpy( mountpoint, ment_ptr->mnt_dir, mountpoint_len - 1 );
         found = true;
         rc = 0;
         break;
      }
      
      ent_count++;
   }
   
   fclose( f );
   
   if( rc == 0 && !found ) {
      fprintf(stderr, "Failed to find mounted sysfs filesystem\n");
      return -ENOSYS;
   }
   
   return rc;
}
コード例 #11
0
ファイル: linux.c プロジェクト: 8l/vdev
// print a uevent, either with debugging or error loglevels
static int vdev_linux_log_uevent( char const* uevent_buf, size_t uevent_buf_len, bool debug ) {
      
   for( unsigned int i = 0; i < uevent_buf_len; ) {
      
      if( debug ) {
         vdev_debug("uevent '%s'\n", uevent_buf + i );
      }
      else {
         vdev_error("uevent '%s'\n", uevent_buf + i );
      }
      
      i += strlen(uevent_buf + i) + 1;
   }
   
   return 0;
}
コード例 #12
0
ファイル: linux.c プロジェクト: 8l/vdev
// parse a device number pair 
// return 0 on success, and set *major and *minor
// return -EINVAL if we failed to parse
static int vdev_linux_sysfs_parse_device_nums( char const* devbuf, unsigned int* major, unsigned int* minor ) {
   
   int rc = 0;
   
   // parse devpath 
   rc = sscanf( devbuf, "%u:%u", major, minor );
   
   if( rc != 2 ) {
      
      vdev_error("sscanf('%s') for major:minor rc = %d\n", devbuf, rc );
      rc = -EINVAL;
   }
   else {
      rc = 0;
   }

   return rc;
}
コード例 #13
0
ファイル: match.c プロジェクト: bjb/vdev
// does a path match any regexes in a list?
// return the index of the match if so (>= 0)
// return the size if not
// return negative on error
int vdev_match_first_regex( char const* path, regex_t* regexes, size_t num_regexes ) {
   
   int matched = 0;
   
   for( unsigned int i = 0; i < num_regexes; i++ ) {
      
      matched = vdev_match_regex( path, &regexes[i] );
      if( matched > 0 ) {
         return i;
      }
      else if( matched < 0 ) {
         vdev_error("vdev_acl_regex_match(%s) rc = %d\n", path, matched );
         return matched;
      }
   }
   
   return num_regexes;
}
コード例 #14
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// creat: create the file as usual, but also write to the underlying filesystem as an emergency counter-measure
// NOTE: since this is backed by FUSE, this handler will only be called for regular files
int vdevfs_create( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, mode_t mode, void** inode_cls, void** handle_cls ) {
   
   int fd = 0;
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   struct fskit_fuse_state* fs_state = fskit_fuse_get_state();
   char const* path = NULL;
   int rc = 0;
   
   rc = vdevfs_access_check( vdev, fs_state, "create", grp->path );
   if( rc < 0 ) {
      
      // denied!
      return -EACCES;
   }
   
   // must be relative path 
   path = grp->path;
   while( *path == '/' && *path != '\0' ) {
      path++;
   }
   
   if( *path == '\0' ) {
      path = ".";
   }
   
   // success!
   fd = openat( vdev->mountpoint_dirfd, path, O_CREAT | O_WRONLY | O_TRUNC, mode );
   if( fd < 0 ) {
      
      fd = -errno;
      vdev_error("openat('%s', '%s') rc = %d\n", vdev->mountpoint, path, fd );
      
      return fd;
   }
   
   // careful...
   void* handle_data = NULL;
   memcpy( &handle_data, &fd, 4 );
   
   *handle_cls = handle_data;
   
   return 0;
}
コード例 #15
0
ファイル: match.c プロジェクト: bjb/vdev
// does a path match a regex?
// return 1 if so, 0 if not, negative on error 
int vdev_match_regex( char const* path, regex_t* regex ) {
   
   int rc = 0;
   
   rc = regexec( regex, path, 0, NULL, 0 );
   
   if( rc != 0 ) {
      if( rc == REG_NOMATCH ) {
         
         // no match 
         return 0;
      }
      else {
         vdev_error("regexec(%s) rc = %d\n", path, rc );
         return -abs(rc);
      }
   }
   
   // match!
   return 1;
}
コード例 #16
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// sync: sync as usual
// NOTE: since this is backed by FUSE, this handler will only be called for regular files
int vdevfs_sync( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent ) {
   
   void* user_data = fskit_entry_get_user_data( fent );
   
   // careful...
   int fd = 0;
   memcpy( &fd, &user_data, 4 );
   
   int rc = 0;
   
   rc = fsync( fd );
   if( rc < 0 ) {
      
      rc = -errno;
      vdev_error("fsync(%d '%s') rc = %d\n", fd, grp->path, rc );
      
      return rc;
   }
   
   return rc;
}
コード例 #17
0
ファイル: common.c プロジェクト: tilt12345678/vdev
// set up a vdev os context
int vdev_os_context_init( struct vdev_os_context* vos, struct vdev_state* state ) {
   
   int rc = 0;
   
   memset( vos, 0, sizeof(struct vdev_os_context) );
   
   vos->state = state;
   
   // set up OS state 
   rc = vdev_os_init( vos, &vos->os_cls );
   if( rc != 0 ) {
      
      vdev_error("vdev_os_init rc = %d\n", rc );
      memset( vos, 0, sizeof(struct vdev_os_context) );
      return rc;
   }
   
   vos->running = true;
   
   return 0;
}
コード例 #18
0
ファイル: main.c プロジェクト: bjb/vdev
// run! 
int main( int argc, char** argv ) {
   
   int rc = 0;
   pid_t pid = 0;
   struct vdevfs vdev;
   
   memset( &vdev, 0, sizeof(struct vdevfs) );
   
   // set up global vdev state
   rc = vdevfs_init( &vdev, argc, argv );
   if( rc != 0 ) {
      
      vdev_error("vdevfs_init rc = %d\n", rc );
      
      exit(1);
   }
   
   // run!
   rc = vdevfs_main( &vdev, vdev.fuse_argc, vdev.fuse_argv );
   
   vdevfs_shutdown( &vdev );
   
   return rc;
}
コード例 #19
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// mkdir: create the directory as normal, but also write to the underlying filesystem as an emergency counter-measure
int vdevfs_mkdir( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, mode_t mode, void** inode_cls ) {
   
   int rc = 0;
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   struct fskit_fuse_state* fs_state = fskit_fuse_get_state();
   char const* path = NULL;
   
   rc = vdevfs_access_check( vdev, fs_state, "mkdir", grp->path );
   if( rc < 0 ) {
      
      // denied!
      return -EACCES;
   }
   
   // must be relative path 
   path = grp->path;
   while( *path == '/' && *path != '\0' ) {
      path++;
   }
   
   if( *path == '\0' ) {
      path = ".";
   }
   
   rc = mkdirat( vdev->mountpoint_dirfd, path, mode );
   
   if( rc != 0 ) {
      
      rc = -errno;
      vdev_error("mkdirat('%s', '%s') rc = %d\n", vdev->mountpoint, path, rc );
      
      return rc;
   }
   
   return 0;
}
コード例 #20
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// get the mountpoint option, by parsing the FUSE command line 
static int vdev_get_mountpoint( int fuse_argc, char** fuse_argv, char** ret_mountpoint ) {
   
   struct fuse_args fargs = FUSE_ARGS_INIT(fuse_argc, fuse_argv);
   char* mountpoint = NULL;
   int unused_1;
   int unused_2;
   int rc = 0;
   
   // parse command-line...
   rc = fuse_parse_cmdline( &fargs, &mountpoint, &unused_1, &unused_2 );
   if( rc < 0 ) {

      vdev_error("fuse_parse_cmdline rc = %d\n", rc );
      fuse_opt_free_args(&fargs);

      return rc;
   }
   
   else {
      
      if( mountpoint != NULL ) {
         
         *ret_mountpoint = strdup( mountpoint );
         free( mountpoint );
         
         rc = 0;
      }
      else {
         rc = -ENOMEM;
      }
      
      fuse_opt_free_args(&fargs);
   }
   
   return 0;
}     
コード例 #21
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// 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;
}
コード例 #22
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// initialize the filesystem front-end 
// call after vdev_init
// return 0 on success
// return -ENOMEM on OOM
// return negative on error
int vdevfs_init( struct vdevfs* vdev, int argc, char** argv ) {
   
   int rc = 0;
   int rh = 0;
   struct fskit_core* core = NULL;
   int fuse_argc = 0;
   char** fuse_argv = NULL;
   int dirfd = 0;
   
   // library setup 
   vdev_setup_global();
   
   struct fskit_fuse_state* fs = VDEV_CALLOC( struct fskit_fuse_state, 1 );
   
   if( fs == NULL ) {
      return -ENOMEM;
   }
   
   fuse_argv = VDEV_CALLOC( char*, argc + 5 );
   
   if( fuse_argv == NULL ) {
      
      free( fs );
      return -ENOMEM;
   }
   
   // load config 
   vdev->config = VDEV_CALLOC( struct vdev_config, 1 );
   if( vdev->config == NULL ) {
      
      free( fs );
      free( fuse_argv );
      return -ENOMEM;
   }
   
   // init config 
   rc = vdev_config_init( vdev->config );
   if( rc != 0 ) {
      
      vdev_error("vdev_config_init rc = %d\n", rc );
      
      vdevfs_shutdown( vdev );
      free( fs );
      free( fuse_argv );
      return rc;
   }
   
   // parse opts 
   rc = vdev_config_load_from_args( vdev->config, argc, argv, &fuse_argc, fuse_argv );
   if( rc != 0 ) {
      
      vdev_error("vdev_opts_parse rc = %d\n", rc );
      
      vdev_config_usage( argv[0] );
      
      free( fs );
      free( fuse_argv );
      vdevfs_shutdown( vdev );
      return rc;
   }
   
   // get the mountpoint, but from FUSE 
   if( vdev->config->mountpoint != NULL ) {
      free( vdev->config->mountpoint );
   }
   
   rc = vdev_get_mountpoint( fuse_argc, fuse_argv, &vdev->config->mountpoint );
   if( rc != 0 ) {
      
      vdev_error("vdev_get_mountpoint rc = %d\n", rc );
      
      vdev_config_usage( argv[0] );
      
      free( fs );
      free( fuse_argv );
      return rc;
   }
   
   vdev_set_debug_level( vdev->config->debug_level );
   vdev_set_error_level( vdev->config->error_level );
   
   vdev_debug("Config file: %s\n", vdev->config->config_path );
   
   rc = vdev_config_load( vdev->config->config_path, vdev->config );
   if( rc != 0 ) {
      
      vdev_error("vdev_config_load('%s') rc = %d\n", vdev->config->config_path, rc );
      
      vdevfs_shutdown( vdev );
      free( fs );
      free( fuse_argv );
      return rc;
   }
   
   vdev_debug("vdev ACLs dir:    %s\n", vdev->config->acls_dir );
   
   // force -odev, since we'll create device nodes 
   fuse_argv[fuse_argc] = (char*)vdev_fuse_odev;
   fuse_argc++;
   
   // force -oallow_other, since we'll want to expose this to everyone 
   fuse_argv[fuse_argc] = (char*)vdev_fuse_allow_other;
   fuse_argc++;
   
   // force -ononempty, since we'll want to import the underlying filesystem
   fuse_argv[fuse_argc] = (char*)vdev_fuse_ononempty;
   fuse_argc++;
   
   vdev->mountpoint = vdev_strdup_or_null( vdev->config->mountpoint );
   
   if( vdev->mountpoint == NULL ) {
      
      vdev_error("Failed to set mountpoint, config.mountpount = '%s'\n", vdev->config->mountpoint );
      
      vdevfs_shutdown( vdev );
      free( fuse_argv );
      free( fs );
      return -EINVAL;
   }
   else {
      
      vdev_debug("mountpoint:       %s\n", vdev->mountpoint );
   }
   
   vdev->argc = argc;
   vdev->argv = argv;
   vdev->fuse_argc = fuse_argc;
   vdev->fuse_argv = fuse_argv;
   
   fskit_set_debug_level( vdev->config->debug_level );
   fskit_set_error_level( vdev->config->error_level );
   
   // get mountpoint directory 
   dirfd = open( vdev->mountpoint, O_DIRECTORY );
   if( dirfd < 0 ) {
      
      rc = -errno;
      vdev_error("open('%s') rc = %d\n", vdev->mountpoint, rc );
      
      free( fs );
      vdevfs_shutdown( vdev );
      return rc;
   }
   
   vdev->mountpoint_dirfd = dirfd;
   
   // set up fskit
   rc = fskit_fuse_init( fs, vdev );
   if( rc != 0 ) {
      
      vdev_error("fskit_fuse_init rc = %d\n", rc );
      free( fs );
      vdevfs_shutdown( vdev );
      return rc;
   }
   
   // load ACLs 
   rc = vdev_acl_load_all( vdev->config->acls_dir, &vdev->acls, &vdev->num_acls );
   if( rc != 0 ) {
      
      vdev_error("vdev_acl_load_all('%s') rc = %d\n", vdev->config->acls_dir, rc );
      
      fskit_fuse_shutdown( fs, NULL );
      free( fs );
      vdevfs_shutdown( vdev );
      return rc;
   }
   
   // make sure the fs can access its methods through the VFS
   fskit_fuse_setting_enable( fs, FSKIT_FUSE_SET_FS_ACCESS );
   
   core = fskit_fuse_get_core( fs );
   
   // add handlers.
   rh = fskit_route_readdir( core, FSKIT_ROUTE_ANY, vdevfs_readdir, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_readdir(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_stat( core, FSKIT_ROUTE_ANY, vdevfs_stat, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_stat(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_mknod( core, FSKIT_ROUTE_ANY, vdevfs_mknod, FSKIT_CONCURRENT );
   if( rc < 0 ) {
      
      vdev_error("fskit_route_mknod(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_mkdir( core, FSKIT_ROUTE_ANY, vdevfs_mkdir, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_mkdir(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_create( core, FSKIT_ROUTE_ANY, vdevfs_create, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_create(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_open( core, FSKIT_ROUTE_ANY, vdevfs_open, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_open(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_read( core, FSKIT_ROUTE_ANY, vdevfs_read, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_read(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_write( core, FSKIT_ROUTE_ANY, vdevfs_write, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_write(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_close( core, FSKIT_ROUTE_ANY, vdevfs_close, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_close(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_sync( core, FSKIT_ROUTE_ANY, vdevfs_sync, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_sync(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   rh = fskit_route_detach( core, FSKIT_ROUTE_ANY, vdevfs_detach, FSKIT_CONCURRENT );
   if( rh < 0 ) {
      
      vdev_error("fskit_route_detach(%s) rc = %d\n", FSKIT_ROUTE_ANY, rh );
      goto vdev_route_fail;
   }
   
   vdev->fs = fs;
   vdev->close_rh = rh;
   
   // set the root to be owned by the effective UID and GID of user
   fskit_chown( core, "/", 0, 0, geteuid(), getegid() );
   
   // import the underlying filesystem once we're mounted, but before taking requests.
   rc = fskit_fuse_postmount_callback( fs, vdevfs_dev_import, vdev );
   if( rc != 0 ) {
      
      vdev_error("fskit_fuse_postmount_callback() rc = %d\n", rc );
      
      vdev->fs = NULL;
      goto vdev_route_fail;
   }
   
   return 0;

vdev_route_fail:

   fskit_fuse_shutdown( fs, NULL );
   free( fs );
   vdevfs_shutdown( vdev );
   return rh;
}
コード例 #23
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// load the filesystem with metadata from under the mountpoint
// return 0 on success 
// return -ENOMEM on OOM
static int vdevfs_dev_import( struct fskit_fuse_state* fs, void* arg ) {
   
   struct vdevfs* vdev = (struct vdevfs*)arg;
   int rc = 0;
   queue<int> dirfds;           // directory descriptors
   queue<char*> dirpaths;       // relative paths to the mountpoint 
   struct fskit_entry* dir_ent = NULL;
   
   struct vdevfs_scandirat_context scan_context;
   
   char* root = vdev_strdup_or_null("/");
   if( root == NULL ) {
      return -ENOMEM;
   }
   
   int root_fd = dup( vdev->mountpoint_dirfd );
   if( root_fd < 0 ) {
      
      vdev_error("dup(%d) rc = %d\n", vdev->mountpoint_dirfd, rc );
      rc = -errno;
      free( root );
      return rc;
   }
   
   // start at the mountpoint
   try {
      
      dirfds.push( root_fd );
      dirpaths.push( root );
   }
   catch( bad_alloc& ba ) {
      
      free( root );
      return -ENOMEM;
   }
   
   while( dirfds.size() > 0 ) {
      
      // next directory 
      int dirfd = dirfds.front();
      char* dirpath = dirpaths.front();
      
      dirfds.pop();
      dirpaths.pop();
      
      // look up this entry 
      dir_ent = fskit_entry_resolve_path( fskit_fuse_get_core( vdev->fs ), dirpath, 0, 0, true, &rc );
      if( rc != 0 ) {
         
         // shouldn't happen--we're going breadth-first 
         vdev_error("fskit_entry_resolve_path('%s') rc = %d\n", dirpath, rc );
         
         close( dirfd );
         free( dirpath );
         dirpath = NULL;
        
         break;
      }
      
      // make a scan context 
      vdevfs_scandirat_context_init( &scan_context, fskit_fuse_get_core( vdev->fs ), dir_ent, dirpath, &dirfds, &dirpaths );
      
      // scan this directory 
      rc = vdev_load_all_at( dirfd, vdevfs_scandirat_context_callback, &scan_context );
      
      fskit_entry_unlock( dir_ent );
      
      if( rc != 0 ) {
         
         // failed
         vdev_error("vdev_load_all_at(%d, '%s') rc = %d\n", dirfd, dirpath, rc );
      }
      
      close( dirfd );
      free( dirpath );
      dirpath = NULL;
      
      if( rc != 0 ) {
         break;
      }
   }
   
   // free any remaining directory state 
   size_t num_dirfds = dirfds.size();
   for( unsigned int i = 0; i < num_dirfds; i++ ) {
      
      int next_dirfd = dirfds.front();
      dirfds.pop();
      
      close( next_dirfd );
   }
   
   size_t num_dirpaths = dirpaths.size();
   for( unsigned int i = 0; i < num_dirpaths; i++ ) {
      
      char* path = dirpaths.front();
      dirpaths.pop();
      
      free( path );
   }
   
   return rc;
}
コード例 #24
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// readdir: equivocate about which devices exist, depending on who's asking
// omit entries if the ACLs forbid them
int vdevfs_readdir( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, struct fskit_dir_entry** dirents, size_t num_dirents ) {
   
   int rc = 0;
   struct fskit_entry* child = NULL;
   
   // entries to omit in the listing
   vector<int> omitted_idx;
   
   pid_t pid = 0;
   uid_t uid = 0;
   gid_t gid = 0;
   
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   struct fskit_fuse_state* fs_state = fskit_fuse_get_state();
   
   struct stat sb;
   struct pstat ps;
   char* child_path = NULL;
   
   pid = fskit_fuse_get_pid();
   uid = fskit_fuse_get_uid( fs_state );
   gid = fskit_fuse_get_gid( fs_state );
   
   vdev_debug("vdevfs_readdir(%s, %zu) from user %d group %d task %d\n", grp->path, num_dirents, uid, gid, pid );
   
   // see who's asking
   rc = pstat( pid, &ps, 0 );
   if( rc != 0 ) { 
      
      vdev_error("pstat(%d) rc = %d\n", pid, rc );
      return -EIO;
   }
   
   for( unsigned int i = 0; i < num_dirents; i++ ) {
      
      // skip . and ..
      if( strcmp(dirents[i]->name, ".") == 0 || strcmp(dirents[i]->name, "..") == 0 ) {
         continue;
      }
      
      // find the associated fskit_entry
      child = fskit_dir_find_by_name( fent, dirents[i]->name );
      
      if( child == NULL ) {
         // strange, shouldn't happen...
         continue;
      }
      
      fskit_entry_rlock( child );
      
      // construct a stat buffer from what we actually need 
      memset( &sb, 0, sizeof(struct stat) );
      
      sb.st_uid = child->owner;
      sb.st_gid = child->group;
      sb.st_mode = fskit_fullmode( child->type, child->mode );
      
      child_path = fskit_fullpath( grp->path, child->name, NULL );
      if( child_path == NULL ) {
         
         // can't continue; OOM
         fskit_entry_unlock( child );
         rc = -ENOMEM;
         break;
      }
      
      // filter it 
      rc = vdev_acl_apply_all( vdev->config, vdev->acls, vdev->num_acls, child_path, &ps, uid, gid, &sb );
      if( rc < 0 ) {
         
         vdev_error("vdev_acl_apply_all('%s', uid=%d, gid=%d, pid=%d) rc = %d\n", child_path, uid, gid, pid, rc );
         rc = -EIO;
      }
      else if( rc == 0 || (sb.st_mode & 0777) == 0 ) {
         
         // omit this one 
         vdev_debug("Filter '%s'\n", child->name );
         omitted_idx.push_back( i );
         
         rc = 0;
      }
      else {
         
         // success; matched
         rc = 0;
      }
      
      fskit_entry_unlock( child );
      
      free( child_path );
      
      // error?
      if( rc != 0 ) {
         break;
      }
   }
   
   // skip ACL'ed entries
   for( unsigned int i = 0; i < omitted_idx.size(); i++ ) {
      
      fskit_readdir_omit( dirents, omitted_idx[i] );
   }
   
   return rc;
}
コード例 #25
0
ファイル: fs.c プロジェクト: bjb/vdev
// 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;
   }
コード例 #26
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// open: open the file as usual, but from the underlying filesystem 
// NOTE: since this is backed by FUSE, this handler will only be called for regular files
int vdevfs_open( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, int flags, void** handle_cls ) {
   
   int fd = 0;
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   struct fskit_fuse_state* fs_state = fskit_fuse_get_state();
   char const* path = NULL;
   
   int rc = 0;
   
   // dir or file?
   char const* method = NULL;
   
   if( fskit_entry_get_type( fent ) == FSKIT_ENTRY_TYPE_DIR ) {
   
      rc = vdevfs_access_check( vdev, fs_state, "opendir", grp->path );
   }
   else {
      
      rc = vdevfs_access_check( vdev, fs_state, "open", grp->path );
   }
   
   if( rc < 0 ) {
      
      // denied!
      return -ENOENT;
   }
   
   // only care about open() for files 
   if( fskit_entry_get_type( fent ) == FSKIT_ENTRY_TYPE_DIR ) {
      
      // done!
      return 0;
   }
   
   // must be relative path 
   path = grp->path;
   while( *path == '/' && *path != '\0' ) {
      path++;
   }
   
   if( *path == '\0' ) {
      path = ".";
   }
   
   fd = openat( vdev->mountpoint_dirfd, path, flags );
   if( fd < 0 ) {
      
      fd = -errno;
      vdev_error("openat(%d, '%s') rc = %d\n", vdev->mountpoint_dirfd, path, fd );
      
      return fd;
   }
   
   // careful...
   void* handle_data = NULL;
   memcpy( &handle_data, &fd, 4 );
   
   *handle_cls = handle_data;
   
   return 0;
}
コード例 #27
0
ファイル: linux.c プロジェクト: 8l/vdev
// yield the next device event
// return 0 on success 
// return 1 if there are no more devices
// return -EAGAIN if vdev should try to get this device again 
// return -errno on failure to poll for devices or read the next device packet.
int vdev_os_next_device( struct vdev_device_request* vreq, void* cls ) {
   
   int rc = 0;
   struct vdev_linux_context* ctx = (struct vdev_linux_context*)cls;
   char buf[VDEV_LINUX_NETLINK_BUF_MAX];
   ssize_t len = 0;
   
   char cbuf[CMSG_SPACE(sizeof(struct ucred))];
   struct cmsghdr *chdr = NULL;
   struct ucred *cred = NULL;
   struct msghdr hdr;
   struct iovec iov;
   struct sockaddr_nl cnls;
   
   pthread_mutex_lock( &ctx->initial_requests_lock );
   
   // do we have initial requests?
   if( ctx->initial_requests != NULL ) {
      
      // next request
      struct vdev_device_request* req = ctx->initial_requests;
      
      // consume 
      ctx->initial_requests = ctx->initial_requests->next;
      
      memcpy( vreq, req, sizeof(struct vdev_device_request) );
      free( req );
      
      pthread_mutex_unlock( &ctx->initial_requests_lock );
      
      // was that the last of them?
      if( ctx->initial_requests == NULL ) {
         
         // tell vdevd that we've flushed all pending requests 
         vdev_os_context_signal_flushed( ctx->os_ctx );
      }
      
      return 0;
   }
   else if( ctx->os_ctx->state->once ) {
      
      // out of requests; die 
      pthread_mutex_unlock( &ctx->initial_requests_lock );
      return 1;
   }
   else {
      
      pthread_mutex_unlock( &ctx->initial_requests_lock );
   }
   
   memset(&hdr, 0, sizeof(struct msghdr));
   
   // next event (wait forever)
   // NOTE: this is a cancellation point!
   rc = poll( &ctx->pfd, 1, -1 );
   
   if( rc < 0 ) {
      
      rc = -errno;
      
      if( rc == -EINTR ) {
         // try again 
         return -EAGAIN;
      }
      
      vdev_error("FATAL: poll(%d) rc = %d\n", ctx->pfd.fd, rc );
      
      return rc;
   }
   
   // get the event 
   iov.iov_base = buf;
   iov.iov_len = VDEV_LINUX_NETLINK_BUF_MAX;
                
   hdr.msg_iov = &iov;
   hdr.msg_iovlen = 1;
   
   // get control-plane messages
   hdr.msg_control = cbuf;
   hdr.msg_controllen = sizeof(cbuf);
   
   hdr.msg_name = &cnls;
   hdr.msg_namelen = sizeof(cnls);

   // get the event 
   len = recvmsg( ctx->pfd.fd, &hdr, 0 );
   if( len < 0 ) {
      
      rc = -errno;
      vdev_error("FATAL: recvmsg(%d) rc = %d\n", ctx->pfd.fd, rc );
      
      return rc;
   }
   
   // big enough?
   if( len < 32 || len >= VDEV_LINUX_NETLINK_BUF_MAX ) {
      
      vdev_error("Netlink message is %zd bytes; ignoring...\n", len );
      return -EAGAIN;
   }
   
   // control message, for credentials
   chdr = CMSG_FIRSTHDR( &hdr );
   if( chdr == NULL || chdr->cmsg_type != SCM_CREDENTIALS ) {
      
      vdev_error("%s", "Netlink message has no credentials\n");
      return -EAGAIN;
   }
   
   // get the credentials
   cred = (struct ucred *)CMSG_DATA(chdr);
   
   // if not root, ignore 
   if( cred->uid != 0 ) {
      
      vdev_error("Ignoring message from non-root ID %d\n", cred->uid );
      return -EAGAIN;
   }
   
   // if udev, ignore 
   if( memcmp( buf, VDEV_LINUX_NETLINK_UDEV_HEADER, VDEV_LINUX_NETLINK_UDEV_HEADER_LEN ) == 0 ) {
      
      // message from udev; ignore 
      vdev_warn("%s", "Ignoring libudev message\n");
      return -EAGAIN;
   }
   
   // kernel messages don't come from userspace 
   if( cnls.nl_pid > 0 ) {
      
      // from userspace???
      vdev_warn("Ignoring message from PID %d\n", (int)cnls.nl_pid );
      return -EAGAIN;
   }
   
   // parse the event buffer
   vdev_debug("%p from netlink\n", vreq );
   rc = vdev_linux_parse_request( ctx, vreq, buf, len );
   
   if( rc != 0 ) {
      
      vdev_error("vdev_linux_parse_request rc = %d\n", rc );
      
      return -EAGAIN;
   }
   
   return 0;
}
コード例 #28
0
ファイル: common.c プロジェクト: tilt12345678/vdev
// yield new devices 
int vdev_os_main( struct vdev_os_context* vos ) {
   
   int rc = 0;
   
   while( vos->running ) {
      
      // make a device request
      struct vdev_device_request* vreq = VDEV_CALLOC( struct vdev_device_request, 1 );
      
      if( vreq == NULL ) {
         // OOM
         break;
      }
      
      // next device request
      rc = vdev_device_request_init( vreq, vos->state, VDEV_DEVICE_INVALID, NULL );
      if( rc != 0 ) {
         
         if( rc == -EAGAIN ) {
            continue;
         }
         
         free( vreq );
         
         vdev_error("vdev_device_request_init rc = %d\n", rc );
         break;
      }
      
      // yield the next device 
      rc = vdev_os_next_device( vreq, vos->os_cls );
      if( rc != 0 ) {
         
         vdev_device_request_free( vreq );
         free( vreq );
         
         if( rc < 0 ) {
            vdev_error("vdev_os_next_device rc = %d\n", rc );
         
            if( rc == -EAGAIN ) {
            
               // OS backend says try again
               continue;
            }
            else {
            
               // fatal error
               break;
            }
         }
         else {
            
            // exit on success 
            rc = 0;
            break;
         }
      }
      
      vdev_debug("Next device: %p, type=%d path=%s major=%u minor=%u mode=%o\n", vreq, vreq->type, vreq->path, major(vreq->dev), minor(vreq->dev), vreq->mode );
      
      /*
      struct sglib_vdev_params_iterator itr2;
      struct vdev_param_t* dp2 = NULL;
      
      printf("vreq %p: params:\n", vreq);
      for( dp2 = sglib_vdev_params_it_init_inorder( &itr2, vreq->params ); dp2 != NULL; dp2 = sglib_vdev_params_it_next( &itr2 ) ) {
         
         printf("   '%s' == '%s'\n", dp2->key, dp2->value );
      }
      */
      
      // post the event to the device work queue
      rc = vdev_device_request_enqueue( &vos->state->device_wq, vreq );
      
      if( rc != 0 ) {
         
         vdev_device_request_free( vreq );
         free( vreq );
         
         vdev_error("vdev_device_request_add rc = %d\n", rc );
         
         continue;
      }
   }
   
   return rc;
}
コード例 #29
0
ファイル: linux.c プロジェクト: 8l/vdev
// parse a uevent, and use the information to fill in a device request.
// nlbuf must be a contiguous concatenation of null-terminated KEY=VALUE strings.
// return 0 on success
static int vdev_linux_parse_request( struct vdev_linux_context* ctx, struct vdev_device_request* vreq, char* nlbuf, ssize_t buflen ) {
   
   char* buf = nlbuf;
   char* key = NULL;
   char* value = NULL;
   int offset = 0;
   int rc = 0;
   unsigned int major = 0;
   unsigned int minor = 0;
   bool have_major = false;
   bool have_minor = false;
   mode_t dev_mode = 0;
   int line_count = 0;
   bool not_param = false;      // if set to true, add as an OS-specific parameter to the vreq
   
   char* devpath = NULL;        // sysfs devpath 
   char* subsystem = NULL;      // sysfs subsystem 
   char* devname = (char*)VDEV_DEVICE_PATH_UNKNOWN;        // DEVNAME from uevent
   
   vdev_device_request_t reqtype = VDEV_DEVICE_INVALID;
   
   vdev_debug("%p: uevent buffer\n", vreq );
   vdev_linux_debug_uevent( nlbuf, buflen );
   
   // sanity check: if the first line is $action@$devpath, then skip it (since the information 
   // contained in the uevent will encode the very same bits of information)
   if( strchr(buf, '@') != NULL ) { 
         
      // advance to the next line
      offset += strlen(buf) + 1;
   }
   
   // get key/value pairs
   while( offset < buflen ) {
      
      line_count++;
      not_param = false;
      
      rc = vdev_keyvalue_next( buf + offset, &key, &value );
      
      if( rc < 0 ) {
         
         vdev_error("Invalid line %d (byte %d): '%s'\n", line_count, offset, buf + offset );
         vdev_linux_error_uevent( nlbuf, buflen );
         
         return -EINVAL;
      }
      
      offset += rc + 1;         // count the \0 at the end
      rc = 0;
      
      // is this the action to take?
      if( strcmp(key, "ACTION") == 0 ) {
         
         reqtype = vdev_linux_parse_device_request_type( value );
         
         if( reqtype == VDEV_DEVICE_INVALID ) {
            
            vdev_error("Invalid ACTION '%s'\n", value );
            vdev_linux_error_uevent( nlbuf, buflen );
            
            return -EINVAL;
         }
         
         vdev_device_request_set_type( vreq, reqtype );
         
         not_param = true;
      }
      
      // is this the sysfs device path?
      else if( strcmp(key, "DEVPATH") == 0 ) {
         
         devpath = value;
      }
      
      // is this the devname?
      else if( strcmp(key, "DEVNAME") == 0 ) {
         
         devname = value;
      }
      
      // subsystem given?
      else if( strcmp(key, "SUBSYSTEM") == 0 ) {
         
         subsystem = vdev_strdup_or_null( value );
      }
      
      // is this the major device number?
      else if( strcmp(key, "MAJOR") == 0 && !have_major ) {
         
         char* tmp = NULL;
         major = (int)strtol( value, &tmp, 10 );
         
         if( *tmp != '\0' ) {
            
            vdev_error("Invalid 'MAJOR' value '%s'\n", value);
            vdev_linux_error_uevent( nlbuf, buflen );
            
            return -EINVAL;
         }
         
         have_major = true;
         not_param = true;
      }
      
      // is this the minor device number?
      else if( strcmp(key, "MINOR") == 0 && !have_minor ) {
         
         char* tmp = NULL;
         minor = (int)strtol( value, &tmp, 10 ) ;
         
         if( *tmp != '\0' ) {
            
            vdev_error("Invalid 'MINOR' value '%s'\n", value );
            vdev_linux_error_uevent( nlbuf, buflen );
            
            return -EINVAL;
         }
         
         have_minor = true;
         not_param = true;
      }
      
      if( !not_param ) {
         
         // add to OS params 
         rc = vdev_device_request_add_param( vreq, key, value );
         if( rc != 0 ) {
            
            // could be OOM 
            if( subsystem != NULL ) {
               free( subsystem );
            }
            
            return rc;
         }
      }
   }
   
   if( reqtype == VDEV_DEVICE_INVALID ) {
      
      vdev_error("%s", "No ACTION given\n");
      vdev_linux_error_uevent( nlbuf, buflen );
      
      if( subsystem != NULL ) {
         free( subsystem );
      }
      
      return -EINVAL;
   }
   
   if( (!have_major && have_minor) || (have_major && !have_minor) ) {
      
      vdev_error("Missing device information: major=%d, minor=%d\n", have_major, have_minor );
      vdev_linux_error_uevent( nlbuf, buflen );
      
      if( subsystem != NULL ) {
         free( subsystem );
      }
      
      return -EINVAL;
   }
   
   if( have_major && have_minor ) {
      
      // explicit major and minor device numbers given 
      vdev_device_request_set_dev( vreq, makedev(major, minor) );
   }
   
   if( devname != NULL ) {
      
      // use this as the device's path 
      vdev_device_request_set_path( vreq, devname );
   }
   
   if( devpath != NULL ) {
      
      // get any remaining information from sysfs 
      // check major/minor?
      if( !have_major || !have_minor ) {
         
         // see if we have major/minor device numbers for this device...
         rc = vdev_linux_sysfs_read_dev_nums( ctx, devpath, &major, &minor );
         
         if( rc == 0 ) {
            
            // yup!
            vdev_device_request_set_dev( vreq, makedev(major, minor) );
            
            have_major = true;
            have_minor = true;
         }
         else {
            
            // it's okay to not have dev numbers
            rc = 0;
         }
      }
      
      // subsystem?
      if( subsystem == NULL ) {
         
         // see if we have a subsystem 
         rc = vdev_linux_sysfs_read_subsystem( ctx, devpath, &subsystem );
         
         if( rc == 0 ) {
            
            // yup!
            rc = vdev_device_request_add_param( vreq, "SUBSYSTEM", subsystem );
            if( rc != 0 ) {
               
               // OOM
               free( subsystem );
               return rc;
            }
         }
         else if( rc != -ENOMEM ) {
            
            // this is weird...
            vdev_warn("no subsystem found for '%s'\n", devpath );
            rc = 0;
         }
      }
   }
   
   if( have_major && have_minor ) {
      
      if( subsystem != NULL && strcasecmp(subsystem, "block") == 0 ) {
         
         // this is a block 
         dev_mode = S_IFBLK;
      }
      
      else {
         
         // this is a character device--we have major/minor numbers
         dev_mode = S_IFCHR;
      }
   
      vdev_device_request_set_mode( vreq, dev_mode );
   }
   
   vdev_debug("subsystem = '%s', have_major=%d, major = %u, have_minor=%d, minor = %u, mode = %o\n", subsystem, have_major, major, have_minor, minor, dev_mode );
   
   if( subsystem != NULL ) {
      free( subsystem );
   }
   
   // tell helpers where /sys is mounted 
   rc = vdev_device_request_add_param( vreq, "SYSFS_MOUNTPOINT", ctx->sysfs_mountpoint );
   if( rc != 0 ) {
      
      // OOM 
      return rc;
   }
   
   return rc;
}
コード例 #30
0
ファイル: linux.c プロジェクト: 8l/vdev
// get a uevent from a uevent file 
// replace newlines with '\0', making the uevent look like it came from the netlink socket
// (i.e. so it can be parsed by vdev_linux_parse_request)
// return 0 on success
// return -ENOMEM on OOM
// return -errno on failure to stat or read
static int vdev_linux_sysfs_read_uevent( char const* fp_uevent, char** ret_uevent_buf, size_t* ret_uevent_len ) {
   
   int rc = 0;
   struct stat sb;
   char* uevent_buf = NULL;
   size_t uevent_buf_len = 0;
   size_t uevent_len = 0;
   
   // get uevent size  
   rc = stat( fp_uevent, &sb );
   if( rc != 0 ) {
      
      rc = -errno;
      
      vdev_error("stat('%s') rc = %d\n", fp_uevent, rc );
      
      return rc;
   }
   else {
      
      uevent_buf_len = sb.st_size;
   }
   
   // read the uevent
   if( fp_uevent != NULL ) {
      
      uevent_buf = VDEV_CALLOC( char, uevent_buf_len );
      if( uevent_buf == NULL ) {
         
         return -ENOMEM;
      }
      
      rc = vdev_read_file( fp_uevent, uevent_buf, uevent_buf_len );
      if( rc != 0 ) {
         
         // failed in this 
         vdev_error("vdev_read_file('%s') rc = %d\n", fp_uevent, rc );
         free( uevent_buf );
      }
      else {
         
         for( unsigned int i = 0; i < uevent_buf_len; i++ ) {
            
            if( uevent_buf[i] == '\n' ) {
               
               uevent_buf[i] = '\0';
            }
         }
         
         // NOTE: the stat size is an upper-bound.  Find the exact number of bytes.
         for( uevent_len = 0; uevent_len < uevent_buf_len; ) {
            
            if( *(uevent_buf + uevent_len) == '\0' ) {
               break;
            }
            
            uevent_len += strlen( uevent_buf + uevent_len ) + 1;
         }
          
         *ret_uevent_buf = uevent_buf;
         *ret_uevent_len = uevent_len;
      }
   }