/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DEVINFO_DEVLINK_H #define _DEVINFO_DEVLINK_H #ifdef __cplusplus extern "C" { #endif #define _POSIX_PTHREAD_SEMANTICS /* For readdir_r */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct db_link { uint32_t attr; /* primary or secondary */ uint32_t path; /* link path */ uint32_t content; /* link content */ uint32_t sib; /* next link for same minor */ }; struct db_minor { uint32_t name; /* minor name */ uint32_t nodetype; /* minor node type */ uint32_t sib; /* next minor for same node */ uint32_t link; /* next minor for same node */ }; struct db_node { uint32_t path; /* node path */ uint32_t sib; /* node's sibling */ uint32_t child; /* first child for this node */ uint32_t minor; /* first minor for node */ }; typedef enum db_seg { DB_NODE = 0, DB_MINOR, DB_LINK, DB_STR, DB_TYPES, /* Number of non-header segments */ DB_HEADER } db_seg_t; struct db_hdr { uint32_t magic; /* Magic number */ uint32_t vers; /* database format version */ uint32_t root_idx; /* index for root node */ uint32_t dngl_idx; /* head of DB dangling links */ uint32_t page_sz; /* page size for mmap alignment */ uint32_t update_count; /* updates since last /dev synch up */ uint32_t nelems[DB_TYPES]; /* Number of elements of each type */ }; typedef struct cache_link { char *path; /* link path */ char *content; /* link content */ uint_t attr; /* link attributes */ struct cache_link *hash; /* next link on same hash chain */ struct cache_link *sib; /* next link for same minor */ struct cache_minor *minor; /* minor for this link */ } cache_link_t; typedef struct cache_minor { char *name; /* minor name */ char *nodetype; /* minor nodetype */ struct cache_node *node; /* node for this minor */ struct cache_minor *sib; /* next minor for same node */ struct cache_link *link; /* first link pointing to minor */ } cache_minor_t; typedef struct cache_node { char *path; /* path */ struct cache_node *parent; /* node's parent */ struct cache_node *sib; /* node's sibling */ struct cache_node *child; /* first child for this node */ struct cache_minor *minor; /* first minor for node */ } cache_node_t; struct cache { uint_t flags; /* cache state */ uint_t update_count; /* updates since /dev synchronization */ uint_t hash_sz; /* number of hash chains */ cache_link_t **hash; /* hash table */ cache_node_t *root; /* root of cache tree */ cache_link_t *dngl; /* list of dangling links */ cache_minor_t *last_minor; /* last minor looked up */ }; struct db { int db_fd; /* database file */ uint_t flags; /* database open mode */ struct db_hdr *hdr; /* DB header */ int seg_prot[DB_TYPES]; /* protection for segments */ caddr_t seg_base[DB_TYPES]; /* base address for segments */ }; struct di_devlink_handle { char *dev_dir; /* /dev */ char *db_dir; /* /etc/dev */ uint_t flags; /* handle flags */ uint_t error; /* records errors encountered */ int lock_fd; /* lock file for updates */ struct cache cache; struct db db; }; typedef struct link_desc { regex_t *regp; const char *minor_path; uint_t flags; void *arg; int (*fcn)(di_devlink_t, void *); int retval; } link_desc_t; struct tnode { void *node; int flags; struct di_devlink_handle *handle; }; struct di_devlink { char *rel_path; char *abs_path; char *content; int type; }; typedef struct recurse { void *data; int (*fcn)(struct di_devlink_handle *, void *, const char *); } recurse_t; /* * Debug levels currently defined. */ typedef enum { DBG_ERR = 1, DBG_LCK, DBG_INFO, DBG_STEP, DBG_ALL } debug_level_t; #define DB_MAGIC 0xBAC2ACAB #define DB_FILE ".devlink_db" #define DB_TMP ".devlink_db_tmp" #define DB_LOCK ".devlink_db_lock" #define DB_PERMS (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR) #define DB_LOCK_PERMS DB_PERMS #define DB_VERSION 1 #define DB_NIL 0 #define DEV "/dev" #define ETCDEV "/etc/dev" #define DEVICES_SUFFIX "ices" #define HDR_LEN sizeof (struct db_hdr) #define AVG_CHAIN_SIZE 20 /* Average number of links per chain */ #define MIN_HASH_SIZE 1024 /* Min number of chains in hash table */ #define MAX_UPDATE_INTERVAL 5 /* Max DB writes before synching with /dev */ #define MAX_LOCK_RETRY 5 /* Max attempts at locking the update lock */ /* * Various flags private to the implementation */ #define A_PRIMARY 0x0001U #define A_SECONDARY 0x0002U #define A_LINK_TYPES 0x0003U /* Mask */ #define A_VALID 0x0004U #define TYPE_DB 0x0008U #define TYPE_CACHE 0x0010U #define CREATE_FLAG 0x0020U #define INSERT_HEAD 0x0040U #define INSERT_TAIL 0x0080U #define OPEN_RDWR 0x0100U #define OPEN_RDONLY 0x0200U #define OPEN_FLAGS 0x0300U /* Mask */ #define UNLINK_FROM_HASH 0x0400U #define SET_VALID_ATTR(a) ((a) |= A_VALID) #define CLR_VALID_ATTR(a) ((a) &= ~A_VALID) #define GET_VALID_ATTR(a) ((a) & A_VALID) #define SET_DB_ERR(h) ((h)->error = 1) #define DB_ERR(h) ((h)->error) #define LOOKUP_DB(f) ((f) & TYPE_DB) #define LOOKUP_CACHE(f) ((f) & TYPE_CACHE) #define CREATE_ELEM(f) ((f) & CREATE_FLAG) #define IS_RDWR(f) (((f) & OPEN_FLAGS) == OPEN_RDWR) #define IS_RDONLY(f) (((f) & OPEN_FLAGS) == OPEN_RDONLY) #define HDL_RDWR(h) (((h)->flags & OPEN_FLAGS) == OPEN_RDWR) #define HDL_RDONLY(h) (((h)->flags & OPEN_FLAGS) == OPEN_RDONLY) #define CACHE(h) (&(h)->cache) #define CACHE_ROOT(h) (CACHE(h)->root) #define CACHE_HASH(h, i) (CACHE(h)->hash[i]) #define CACHE_LAST(h) (CACHE(h)->last_minor) #define CACHE_EMPTY(h) (CACHE(h)->root == NULL && CACHE(h)->dngl == NULL) #define DB(h) (&(h)->db) #define DB_HDR(h) (DB(h)->hdr) #define DB_NUM(h, t) (DB_HDR(h)->nelems[t]) #define DB_SEG(h, t) (DB(h)->seg_base[t]) #define DB_SEG_PROT(h, t) (DB(h)->seg_prot[t]) #define DB_OPEN(h) (DB_HDR(h) != NULL) #define DB_RDWR(h) ((DB(h)->flags & OPEN_FLAGS) == OPEN_RDWR) #define DB_RDONLY(h) ((DB(h)->flags & OPEN_FLAGS) == OPEN_RDONLY) #define DB_EMPTY(h) (DB_HDR(h)->root_idx == DB_NIL && \ DB_HDR(h)->dngl_idx == DB_NIL) #define TYPE_NONE(f) (((f) & DI_LINK_TYPES) == 0) #define TYPE_PRI(f) (((f) & DI_LINK_TYPES) == DI_PRIMARY_LINK) #define TYPE_SEC(f) (((f) & DI_LINK_TYPES) == DI_SECONDARY_LINK) #define LINK_TYPE(f) ((f) & DI_LINK_TYPES) #define VALID_TYPE(f) (TYPE_NONE(f) || TYPE_PRI(f) || TYPE_SEC(f)) #define VALID_STR(h, i, s) ((i) + strlen(s) + 1 <= DB_HDR(h)->nelems[DB_STR]) #define VALID_INDEX(h, t, i) ((i) < DB_HDR(h)->nelems[t]) /* * Environment variables used by DEBUG version of code. */ #define SKIP_DB "DEBUG_SKIP_DB" #define SKIP_LAST_CACHE "DEBUG_SKIP_LAST_CACHE" #define ALT_DB_DIR "DEBUG_ALT_DB_DIR" /* * Function prototypes */ static struct di_devlink_handle *handle_alloc(const char *dev_dir, uint_t flags); static int cache_alloc(struct di_devlink_handle *hdp); static int open_db(struct di_devlink_handle *hdp, int flags); static int invalid_db(struct di_devlink_handle *hdp, size_t fsize, long pg_sz); static int read_nodes(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx); static int read_minors(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx); static int read_links(struct di_devlink_handle *hdp, cache_minor_t *pcmp, uint32_t nidx); static int init_hdr(struct di_devlink_handle *hdp, long page_sz, uint32_t *count); static size_t size_db(struct di_devlink_handle *hdp, long page_sz, uint32_t *count); static size_t seg_size(struct di_devlink_handle *hdp, int seg); static cache_node_t *node_insert(struct di_devlink_handle *hdp, cache_node_t *pcnp, const char *path, int insert); static cache_minor_t *minor_insert(struct di_devlink_handle *hdp, cache_node_t *pcnp, const char *name, const char *nodetype, cache_minor_t **prev); static cache_link_t *link_insert(struct di_devlink_handle *hdp, cache_minor_t *mnp, const char *path, const char *content, uint32_t attr); static void minor_delete(di_devlink_handle_t hdp, cache_minor_t *cmnp); static void link_delete(di_devlink_handle_t hdp, cache_link_t *clp); static int write_nodes(struct di_devlink_handle *hdp, struct db_node *pdnp, cache_node_t *cnp, uint32_t *next); static int write_minors(struct di_devlink_handle *hdp, struct db_node *pdnp, cache_minor_t *cmnp, uint32_t *next); static int write_links(struct di_devlink_handle *hdp, struct db_minor *pdmp, cache_link_t *clp, uint32_t *next); static void rm_link_from_hash(struct di_devlink_handle *hdp, cache_link_t *clp); static uint32_t write_string(struct di_devlink_handle *hdp, const char *str, uint32_t *next); static int close_db(struct di_devlink_handle *hdp); static void cache_free(struct di_devlink_handle *hdp); static void handle_free(struct di_devlink_handle **pp); static void resolve_dangling_links(struct di_devlink_handle *hdp); static void subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp); static void node_free(cache_node_t **pp); static void minor_free(struct di_devlink_handle *hdp, cache_minor_t **pp); static void link_free(cache_link_t **pp); static void count_node(cache_node_t *cnp, uint32_t *count); static void count_minor(cache_minor_t *mnp, uint32_t *count); static void count_link(cache_link_t *clp, uint32_t *count); static void count_string(const char *str, uint32_t *count); static int visit_node(const char *path, void *arg); static int walk_tree(char *cur, void *arg, int (*node_callback)(const char *path, void *arg)); static void *lookup_node(struct di_devlink_handle *hdp, char *path, const int flags); static cache_link_t *add_link(struct di_devlink_handle *hdp, const char *link, const char *content, int primary); static void *lookup_minor(struct di_devlink_handle *hdp, const char *minor_path, const char *nodetype, const int flags); static cache_link_t *link_hash(di_devlink_handle_t hdp, const char *link, uint_t flags); static void hash_insert(struct di_devlink_handle *hdp, cache_link_t *clp); static uint_t hashfn(struct di_devlink_handle *hdp, const char *str); static void get_db_path(struct di_devlink_handle *hdp, const char *fname, char *buf, size_t blen); static struct db_node *get_node(struct di_devlink_handle *hdp, uint32_t idx); static struct db_node *set_node(struct di_devlink_handle *hdp, uint32_t idx); static struct db_minor *get_minor(struct di_devlink_handle *hdp, uint32_t idx); static struct db_minor *set_minor(struct di_devlink_handle *hdp, uint32_t idx); static struct db_link *get_link(struct di_devlink_handle *hdp, uint32_t idx); static struct db_link *set_link(struct di_devlink_handle *hdp, uint32_t idx); static char *get_string(struct di_devlink_handle *hdp, uint32_t idx); static char *set_string(struct di_devlink_handle *hdp, uint32_t idx); static void *map_seg(struct di_devlink_handle *hdp, uint32_t idx, int prot, db_seg_t seg); static int walk_db(struct di_devlink_handle *hdp, link_desc_t *linkp); static int walk_all_links(struct di_devlink_handle *hdp, link_desc_t *linkp); static int walk_matching_links(struct di_devlink_handle *hdp, link_desc_t *linkp); static int visit_link(struct di_devlink_handle *hdp, link_desc_t *linkp, struct di_devlink *vlp); static void walk_cache_minor(di_devlink_handle_t hdp, const char *mpath, link_desc_t *linkp); static int walk_cache_links(di_devlink_handle_t hdp, cache_link_t *clp, link_desc_t *linkp); static void walk_all_cache(di_devlink_handle_t hdp, link_desc_t *linkp); static int cache_dev_link(struct di_devlink_handle *hdp, void *data, const char *link_path); static int walk_dev(struct di_devlink_handle *hdp, link_desc_t *linkp); static int recurse_dev(struct di_devlink_handle *hdp, recurse_t *rp); static int do_recurse(const char *dir, struct di_devlink_handle *hdp, recurse_t *rp, int *retp); static int check_attr(uint32_t attr); static int attr2type(uint32_t attr); static int check_args(link_desc_t *linkp); static void *get_last_node(struct di_devlink_handle *hdp, const char *path, int flags); static void *get_last_minor(struct di_devlink_handle *hdp, const char *devfs_path, const char *minor_name, int flags); static void set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp, int flags); static int enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir); static void exit_db_lock(struct di_devlink_handle *hdp); static char *minor_colon(const char *path); static const char *rel_path(struct di_devlink_handle *hdp, const char *path); static int link_flag(uint_t flags); static int s_readlink(const char *link, char *buf, size_t blen); static cache_minor_t *link2minor(struct di_devlink_handle *hdp, cache_link_t *clp); static int link_cmp(cache_link_t *clp, const char *content, int type); static void delete_unused_nodes(di_devlink_handle_t hdp, cache_node_t *cnp); static void delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp); static int synchronize_db(di_devlink_handle_t hdp); static void dprintf(debug_level_t msglevel, const char *fmt, ...); static di_devlink_handle_t devlink_snapshot(const char *root_dir); static int devlink_create(const char *root, const char *name, int dca_flags); static int dca_init(const char *name, struct dca_off *dcp, int dca_flags); static void exec_cmd(const char *root, struct dca_off *dcp); static int do_exec(const char *path, char *const argv[]); static int start_daemon(const char *root, int install); static int daemon_call(const char *root, struct dca_off *dcp); int is_minor_node(const char *contents, const char **mn_root); char *s_realpath(const char *path, char *resolved_path); #ifdef __cplusplus } #endif #endif /* _DEVINFO_DEVLINK_H */