I hereby relicense all my work on Sortix under the ISC license as below. All Sortix contributions by other people are already under this license, are not substantial enough to be copyrightable, or have been removed. All imported code from other projects is compatible with this license. All GPL licensed code from other projects had previously been removed. Copyright 2011-2016 Jonas 'Sortie' Termansen and contributors. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166 lines
4.4 KiB
C
166 lines
4.4 KiB
C
/*
|
|
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* devices.c
|
|
* Locate block devices.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <mount/devices.h>
|
|
#include <mount/harddisk.h>
|
|
|
|
static bool is_partition_name(const char* path)
|
|
{
|
|
const char* name = path;
|
|
for ( size_t i = 0; path[i]; i++ )
|
|
if ( path[i] == '/' )
|
|
name = path + i + 1;
|
|
if ( !isalpha((unsigned char) *name) )
|
|
return false;
|
|
name++;
|
|
while ( isalpha((unsigned char) *name) )
|
|
name++;
|
|
if ( !isdigit((unsigned char) *name) )
|
|
return false;
|
|
name++;
|
|
while ( isdigit((unsigned char) *name) )
|
|
name++;
|
|
if ( *name != 'p' )
|
|
return false;
|
|
name++;
|
|
if ( !isdigit((unsigned char) *name) )
|
|
return false;
|
|
name++;
|
|
while ( isdigit((unsigned char) *name) )
|
|
name++;
|
|
return *name == '\0';
|
|
}
|
|
|
|
bool devices_iterate_path(bool (*callback)(void*, const char*), void* ctx)
|
|
{
|
|
const char* dev_path = "/dev";
|
|
DIR* dir = opendir(dev_path);
|
|
if ( !dir )
|
|
return errno == ENOENT;
|
|
// TODO: This readdir loop can be derailed if the callback creates devices.
|
|
struct dirent* entry;
|
|
while ( (errno = 0, entry = readdir(dir)) )
|
|
{
|
|
if ( entry->d_name[0] == '.' )
|
|
continue;
|
|
if ( entry->d_type != DT_UNKNOWN && entry->d_type != DT_BLK )
|
|
continue;
|
|
// TODO: Remove this once partitions identify themselves as such and
|
|
// libmount doesn't allow using them as a real full block device.
|
|
if ( is_partition_name(entry->d_name) )
|
|
continue;
|
|
char* path;
|
|
if ( asprintf(&path, "%s/%s", dev_path, entry->d_name) < 0 )
|
|
return closedir(dir), false;
|
|
bool success = callback(ctx, path);
|
|
free(path);
|
|
if ( !success )
|
|
return closedir(dir), false;
|
|
}
|
|
bool success = errno == 0;
|
|
closedir(dir);
|
|
return success;
|
|
}
|
|
|
|
struct devices_iterate_open
|
|
{
|
|
void* ctx;
|
|
bool (*callback)(void*, struct harddisk*);
|
|
};
|
|
|
|
static bool devices_iterate_open_callback(void* ctx_ptr, const char* path)
|
|
{
|
|
struct devices_iterate_open* ctx = (struct devices_iterate_open*) ctx_ptr;
|
|
struct harddisk* hd = harddisk_openat(AT_FDCWD, path, O_RDWR);
|
|
if ( !hd )
|
|
{
|
|
int true_errno = errno;
|
|
struct stat st;
|
|
if ( lstat(path, 0) == 0 && !S_ISBLK(st.st_mode) )
|
|
return true;
|
|
errno = true_errno;
|
|
return false;
|
|
}
|
|
if ( !harddisk_inspect_blockdevice(hd) )
|
|
{
|
|
bool success = errno == ENOTBLK;
|
|
harddisk_close(hd);
|
|
return success;
|
|
}
|
|
return ctx->callback(ctx->ctx, hd);
|
|
}
|
|
|
|
bool devices_iterate_open(bool (*callback)(void*, struct harddisk*), void* ctx)
|
|
{
|
|
struct devices_iterate_open myctx;
|
|
myctx.ctx = ctx;
|
|
myctx.callback = callback;
|
|
return devices_iterate_path(devices_iterate_open_callback, &myctx);
|
|
}
|
|
|
|
struct devices_open_all
|
|
{
|
|
struct harddisk** hds;
|
|
size_t hds_count;
|
|
size_t hds_allocated;
|
|
};
|
|
|
|
static bool devices_open_all_callback(void* ctx_ptr, struct harddisk* hd)
|
|
{
|
|
struct devices_open_all* ctx = (struct devices_open_all*) ctx_ptr;
|
|
if ( ctx->hds_count == ctx->hds_allocated )
|
|
{
|
|
size_t new_allocated = ctx->hds_allocated ? 2 * ctx->hds_allocated : 16;
|
|
struct harddisk** new_hds = (struct harddisk**)
|
|
reallocarray(ctx->hds, new_allocated, sizeof(struct harddisk*));
|
|
if ( !new_hds )
|
|
return harddisk_close(hd), false;
|
|
ctx->hds = new_hds;
|
|
ctx->hds_allocated = new_allocated;
|
|
}
|
|
ctx->hds[ctx->hds_count++] = hd;
|
|
return true;
|
|
}
|
|
|
|
bool devices_open_all(struct harddisk*** hds_ptr, size_t* hds_count_ptr)
|
|
{
|
|
struct devices_open_all myctx;
|
|
myctx.hds = NULL;
|
|
myctx.hds_count = 0;
|
|
myctx.hds_allocated = 0;
|
|
if ( !devices_iterate_open(devices_open_all_callback, &myctx) )
|
|
{
|
|
for ( size_t i = 0; i < myctx.hds_count; i++ )
|
|
harddisk_close(myctx.hds[i]);
|
|
free(myctx.hds);
|
|
return false;
|
|
}
|
|
*hds_ptr = myctx.hds;
|
|
*hds_count_ptr = myctx.hds_count;
|
|
return true;
|
|
}
|