538 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ProcFS - tree.c - by Alen Stojanov and David van Moolenbroek */
 | |
| 
 | |
| #include "inc.h"
 | |
| 
 | |
| struct proc proc[NR_PROCS + NR_TASKS];
 | |
| struct mproc mproc[NR_PROCS];
 | |
| struct fproc fproc[NR_PROCS];
 | |
| 
 | |
| static int nr_pid_entries;
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				slot_in_use				     *
 | |
|  *===========================================================================*/
 | |
| static int slot_in_use(int slot)
 | |
| {
 | |
| 	/* Return whether the given slot is in use by a process.
 | |
| 	 */
 | |
| 
 | |
| 	/* For kernel tasks, check only the kernel slot. Tasks do not have a
 | |
| 	 * PM/VFS process slot.
 | |
| 	 */
 | |
| 	if (slot < NR_TASKS)
 | |
| 		return (proc[slot].p_rts_flags != RTS_SLOT_FREE);
 | |
| 
 | |
| 	/* For regular processes, check only the PM slot. Do not check the
 | |
| 	 * kernel slot, because that would skip zombie processes. The PID check
 | |
| 	 * should be redundant, but if it fails, procfs could crash.
 | |
| 	 */
 | |
| 	return ((mproc[slot - NR_TASKS].mp_flags & IN_USE) &&
 | |
| 		mproc[slot - NR_TASKS].mp_pid != 0);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				check_owner				     *
 | |
|  *===========================================================================*/
 | |
| static int check_owner(struct inode *node, int slot)
 | |
| {
 | |
| 	/* Check if the owner user and group ID of the inode are still in sync
 | |
| 	 * the current effective user and group ID of the given process.
 | |
| 	 */
 | |
| 	struct inode_stat stat;
 | |
| 
 | |
| 	if (slot < NR_TASKS) return TRUE;
 | |
| 
 | |
| 	get_inode_stat(node, &stat);
 | |
| 
 | |
| 	return (stat.uid == mproc[slot - NR_TASKS].mp_effuid &&
 | |
| 		stat.gid == mproc[slot - NR_TASKS].mp_effgid);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				make_stat				     *
 | |
|  *===========================================================================*/
 | |
| static void make_stat(struct inode_stat *stat, int slot, int index)
 | |
| {
 | |
| 	/* Fill in an inode_stat structure for the given process slot and
 | |
| 	 * per-pid file index (or NO_INDEX for the process subdirectory root).
 | |
| 	 */
 | |
| 
 | |
| 	if (index == NO_INDEX)
 | |
| 		stat->mode = DIR_ALL_MODE;
 | |
| 	else
 | |
| 		stat->mode = pid_files[index].mode;
 | |
| 
 | |
| 	if (slot < NR_TASKS) {
 | |
| 		stat->uid = SUPER_USER;
 | |
| 		stat->gid = SUPER_USER;
 | |
| 	} else {
 | |
| 		stat->uid = mproc[slot - NR_TASKS].mp_effuid;
 | |
| 		stat->gid = mproc[slot - NR_TASKS].mp_effgid;
 | |
| 	}
 | |
| 
 | |
| 	stat->size = 0;
 | |
| 	stat->dev = NO_DEV;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				dir_is_pid				     *
 | |
|  *===========================================================================*/
 | |
| static int dir_is_pid(struct inode *node)
 | |
| {
 | |
| 	/* Return whether the given node is a PID directory.
 | |
| 	 */
 | |
| 
 | |
| 	return (get_parent_inode(node) == get_root_inode() &&
 | |
| 		get_inode_index(node) != NO_INDEX);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				update_proc_table			     *
 | |
|  *===========================================================================*/
 | |
| static int update_proc_table(void)
 | |
| {
 | |
| 	/* Get the process table from the kernel.
 | |
| 	 * Check the magic number in the table entries.
 | |
| 	 */
 | |
| 	int r, slot;
 | |
| 
 | |
| 	if ((r = sys_getproctab(proc)) != OK) return r;
 | |
| 
 | |
| 	for (slot = 0; slot < NR_PROCS + NR_TASKS; slot++) {
 | |
| 		if (proc[slot].p_magic != PMAGIC) {
 | |
| 			printf("PROCFS: system version mismatch!\n");
 | |
| 
 | |
| 			return EINVAL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				update_mproc_table			     *
 | |
|  *===========================================================================*/
 | |
| static int update_mproc_table(void)
 | |
| {
 | |
| 	/* Get the process table from PM.
 | |
| 	 * Check the magic number in the table entries.
 | |
| 	 */
 | |
| 	int r, slot;
 | |
| 
 | |
| 	r = getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc, sizeof(mproc));
 | |
| 	if (r != OK) return r;
 | |
| 
 | |
| 	for (slot = 0; slot < NR_PROCS; slot++) {
 | |
| 		if (mproc[slot].mp_magic != MP_MAGIC) {
 | |
| 			printf("PROCFS: PM version mismatch!\n");
 | |
| 
 | |
| 			return EINVAL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				update_fproc_table			     *
 | |
|  *===========================================================================*/
 | |
| static int update_fproc_table(void)
 | |
| {
 | |
| 	/* Get the process table from VFS.
 | |
| 	 */
 | |
| 
 | |
| 	return getsysinfo(VFS_PROC_NR, SI_PROC_TAB, fproc, sizeof(fproc));
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				update_tables				     *
 | |
|  *===========================================================================*/
 | |
| static int update_tables(void)
 | |
| {
 | |
| 	/* Get the process tables from the kernel, PM, and VFS.
 | |
| 	 */
 | |
| 	int r;
 | |
| 
 | |
| 	if ((r = update_proc_table()) != OK) return r;
 | |
| 
 | |
| 	if ((r = update_mproc_table()) != OK) return r;
 | |
| 
 | |
| 	if ((r = update_fproc_table()) != OK) return r;
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				init_tree				     *
 | |
|  *===========================================================================*/
 | |
| int init_tree(void)
 | |
| {
 | |
| 	/* Initialize this module, before VTreeFS is started. As part of the
 | |
| 	 * process, check if we're not compiled against a kernel different from
 | |
| 	 * the one that is running at the moment.
 | |
| 	 */
 | |
| 	int i, r;
 | |
| 
 | |
| 	if ((r = update_tables()) != OK)
 | |
| 		return r;
 | |
| 
 | |
| 	/* Get the maximum number of entries that we may add to each PID's
 | |
| 	 * directory. We could just default to a large value, but why not get
 | |
| 	 * it right?
 | |
| 	 */
 | |
| 	for (i = 0; pid_files[i].name != NULL; i++);
 | |
| 
 | |
| 	nr_pid_entries = i;
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				out_of_inodes				     *
 | |
|  *===========================================================================*/
 | |
| static void out_of_inodes(void)
 | |
| {
 | |
| 	/* Out of inodes - the NR_INODES value is set too low. We can not do
 | |
| 	 * much, but we might be able to continue with degraded functionality,
 | |
| 	 * so do not panic. If the NR_INODES value is not below the *crucial*
 | |
| 	 * minimum, the symptom of this case will be an incomplete listing of
 | |
| 	 * the main proc directory.
 | |
| 	 */
 | |
| 	static int warned = FALSE;
 | |
| 
 | |
| 	if (warned == FALSE) {
 | |
| 		printf("PROCFS: out of inodes!\n");
 | |
| 
 | |
| 		warned = TRUE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				construct_pid_dirs			     *
 | |
|  *===========================================================================*/
 | |
| static void construct_pid_dirs(void)
 | |
| {
 | |
| 	/* Regenerate the set of PID directories in the root directory of the
 | |
| 	 * file system. Add new directories and delete old directories as
 | |
| 	 * appropriate; leave unchanged those that should remain the same.
 | |
| 	 *
 | |
| 	 * We have to make two passes. Otherwise, we would trigger a vtreefs
 | |
| 	 * assert when we add an entry for a PID before deleting the previous
 | |
| 	 * entry for that PID. While rare, such rapid PID reuse does occur in
 | |
| 	 * practice.
 | |
| 	 */
 | |
| 	struct inode *root, *node;
 | |
| 	struct inode_stat stat;
 | |
| 	char name[PNAME_MAX+1];
 | |
| 	pid_t pid;
 | |
| 	int i;
 | |
| 
 | |
| 	root = get_root_inode();
 | |
| 
 | |
| 	/* First pass: delete old entries. */
 | |
| 	for (i = 0; i < NR_PROCS + NR_TASKS; i++) {
 | |
| 		/* Do we already have an inode associated with this slot? */
 | |
| 		node = get_inode_by_index(root, i);
 | |
| 		if (node == NULL)
 | |
| 			continue;
 | |
| 
 | |
| 		/* If the process slot is not in use, delete the associated
 | |
| 		 * inode.
 | |
| 		 */
 | |
| 		if (!slot_in_use(i)) {
 | |
| 			delete_inode(node);
 | |
| 
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Otherwise, get the process ID. */
 | |
| 		if (i < NR_TASKS)
 | |
| 			pid = (pid_t) (i - NR_TASKS);
 | |
| 		else
 | |
| 			pid = mproc[i - NR_TASKS].mp_pid;
 | |
| 
 | |
| 		/* If there is an old entry, see if the pid matches the current
 | |
| 		 * entry, and the owner is still the same. Otherwise, delete
 | |
| 		 * the old entry first. We reconstruct the entire subtree even
 | |
| 		 * if only the owner changed, for security reasons: if a
 | |
| 		 * process could keep open a file or directory across the owner
 | |
| 		 * change, it might be able to access information it shouldn't.
 | |
| 		 */
 | |
| 		if (pid != (pid_t) get_inode_cbdata(node) ||
 | |
| 				!check_owner(node, i))
 | |
| 			delete_inode(node);
 | |
| 	}
 | |
| 
 | |
| 	/* Second pass: add new entries. */
 | |
| 	for (i = 0; i < NR_PROCS + NR_TASKS; i++) {
 | |
| 		/* If the process slot is not in use, skip this slot. */
 | |
| 		if (!slot_in_use(i))
 | |
| 			continue;
 | |
| 
 | |
| 		/* If we have an inode associated with this slot, we have
 | |
| 		 * already checked it to be up-to-date above.
 | |
| 		 */
 | |
| 		if (get_inode_by_index(root, i) != NULL)
 | |
| 			continue;
 | |
| 
 | |
| 		/* Get the process ID. */
 | |
| 		if (i < NR_TASKS)
 | |
| 			pid = (pid_t) (i - NR_TASKS);
 | |
| 		else
 | |
| 			pid = mproc[i - NR_TASKS].mp_pid;
 | |
| 
 | |
| 		/* Add the entry for the process slot. */
 | |
| 		snprintf(name, PNAME_MAX + 1, "%d", pid);
 | |
| 
 | |
| 		make_stat(&stat, i, NO_INDEX);
 | |
| 
 | |
| 		node = add_inode(root, name, i, &stat, nr_pid_entries,
 | |
| 			(cbdata_t) pid);
 | |
| 
 | |
| 		if (node == NULL)
 | |
| 			out_of_inodes();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				make_one_pid_entry			     *
 | |
|  *===========================================================================*/
 | |
| static void make_one_pid_entry(struct inode *parent, char *name, int slot)
 | |
| {
 | |
| 	/* Construct one file in a PID directory, if a file with the given name
 | |
| 	 * should exist at all.
 | |
| 	 */
 | |
| 	struct inode *node;
 | |
| 	struct inode_stat stat;
 | |
| 	int i;
 | |
| 
 | |
| 	/* Don't readd if it is already there. */
 | |
| 	node = get_inode_by_name(parent, name);
 | |
| 	if (node != NULL)
 | |
| 		return;
 | |
| 
 | |
| 	/* Only add the file if it is a known, registered name. */
 | |
| 	for (i = 0; pid_files[i].name != NULL; i++) {
 | |
| 		if (!strcmp(name, pid_files[i].name)) {
 | |
| 			make_stat(&stat, slot, i);
 | |
| 
 | |
| 			node = add_inode(parent, name, i, &stat,
 | |
| 				(index_t) 0, (cbdata_t) 0);
 | |
| 
 | |
| 			if (node == NULL)
 | |
| 				out_of_inodes();
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				make_all_pid_entries			     *
 | |
|  *===========================================================================*/
 | |
| static void make_all_pid_entries(struct inode *parent, int slot)
 | |
| {
 | |
| 	/* Construct all files in a PID directory.
 | |
| 	 */
 | |
| 	struct inode *node;
 | |
| 	struct inode_stat stat;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; pid_files[i].name != NULL; i++) {
 | |
| 		node = get_inode_by_index(parent, i);
 | |
| 		if (node != NULL)
 | |
| 			continue;
 | |
| 
 | |
| 		make_stat(&stat, slot, i);
 | |
| 
 | |
| 		node = add_inode(parent, pid_files[i].name, i, &stat,
 | |
| 			(index_t) 0, (cbdata_t) 0);
 | |
| 
 | |
| 		if (node == NULL)
 | |
| 			out_of_inodes();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				construct_pid_entries			     *
 | |
|  *===========================================================================*/
 | |
| static void construct_pid_entries(struct inode *parent, char *name)
 | |
| {
 | |
| 	/* Construct one requested file entry, or all file entries, in a PID
 | |
| 	 * directory.
 | |
| 	 */
 | |
| 	int slot;
 | |
| 
 | |
| 	slot = get_inode_index(parent);
 | |
| 	assert(slot >= 0 && slot < NR_TASKS + NR_PROCS);
 | |
| 
 | |
| 	/* If this process is already gone, delete the directory now. */
 | |
| 	if (!slot_in_use(slot)) {
 | |
| 		delete_inode(parent);
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* If a specific file name is being looked up, see if we have to add
 | |
| 	 * an inode for that file. If the directory contents are being
 | |
| 	 * retrieved, add all files that have not yet been added.
 | |
| 	 */
 | |
| 	if (name != NULL)
 | |
| 		make_one_pid_entry(parent, name, slot);
 | |
| 	else
 | |
| 		make_all_pid_entries(parent, slot);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				pid_read				     *
 | |
|  *===========================================================================*/
 | |
| static void pid_read(struct inode *node)
 | |
| {
 | |
| 	/* Data is requested from one of the files in a PID directory. Call the
 | |
| 	 * function that is responsible for generating the data for that file.
 | |
| 	 */
 | |
| 	struct inode *parent;
 | |
| 	int slot, index;
 | |
| 
 | |
| 	/* Get the slot number of the process. Note that this currently will
 | |
| 	 * not work for files not in the top-level pid subdirectory.
 | |
| 	 */
 | |
| 	parent = get_parent_inode(node);
 | |
| 
 | |
| 	slot = get_inode_index(parent);
 | |
| 
 | |
| 	/* Get this file's index number. */
 | |
| 	index = get_inode_index(node);
 | |
| 
 | |
| 	/* Call the handler procedure for the file. */
 | |
| 	((void (*) (int)) pid_files[index].data)(slot);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				pid_link				     *
 | |
|  *===========================================================================*/
 | |
| static int pid_link(struct inode *UNUSED(node), char *ptr, int max)
 | |
| {
 | |
| 	/* The contents of a symbolic link in a PID directory are requested.
 | |
| 	 * This function is a placeholder for future use.
 | |
| 	 */
 | |
| 
 | |
| 	/* Nothing yet. */
 | |
| 	strlcpy(ptr, "", max);
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				lookup_hook				     *
 | |
|  *===========================================================================*/
 | |
| int lookup_hook(struct inode *parent, char *name,
 | |
| 	cbdata_t UNUSED(cbdata))
 | |
| {
 | |
| 	/* Path name resolution hook, for a specific parent and name pair.
 | |
| 	 * If needed, update our own view of the system first; after that,
 | |
| 	 * determine whether we need to (re)generate certain files.
 | |
| 	 */
 | |
| 	static clock_t last_update = 0;
 | |
| 	clock_t now;
 | |
| 	int r;
 | |
| 
 | |
| 	/* Update lazily for lookups, as this gets too expensive otherwise.
 | |
| 	 * Alternative: pull in only PM's table?
 | |
| 	 */
 | |
| 	if ((r = getuptime(&now)) != OK)
 | |
| 		panic(__FILE__, "unable to get uptime", r);
 | |
| 
 | |
| 	if (last_update != now) {
 | |
| 		update_tables();
 | |
| 
 | |
| 		last_update = now;
 | |
| 	}
 | |
| 
 | |
| 	/* If the parent is the root directory, we must now reconstruct all
 | |
| 	 * entries, because some of them might have been garbage collected.
 | |
| 	 * We must update the entire tree at once; if we update individual
 | |
| 	 * entries, we risk name collisions.
 | |
| 	 */
 | |
| 	if (parent == get_root_inode()) {
 | |
| 		construct_pid_dirs();
 | |
| 	}
 | |
| 	/* If the parent is a process directory, we may need to (re)construct
 | |
| 	 * the entry being looked up.
 | |
| 	 */
 | |
| 	else if (dir_is_pid(parent)) {
 | |
| 		/* We might now have deleted our current containing directory;
 | |
| 		 * construct_pid_entries() will take care of this case.
 | |
| 		 */
 | |
| 		construct_pid_entries(parent, name);
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				getdents_hook				     *
 | |
|  *===========================================================================*/
 | |
| int getdents_hook(struct inode *node, cbdata_t UNUSED(cbdata))
 | |
| {
 | |
| 	/* Directory entry retrieval hook, for potentially all files in a
 | |
| 	 * directory. Make sure that all files that are supposed to be
 | |
| 	 * returned, are actually part of the virtual tree.
 | |
| 	 */
 | |
| 
 | |
| 	if (node == get_root_inode()) {
 | |
| 		update_tables();
 | |
| 
 | |
| 		construct_pid_dirs();
 | |
| 	} else if (dir_is_pid(node)) {
 | |
| 		construct_pid_entries(node, NULL /*name*/);
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				read_hook				     *
 | |
|  *===========================================================================*/
 | |
| int read_hook(struct inode *node, off_t off, char **ptr,
 | |
| 	size_t *len, cbdata_t cbdata)
 | |
| {
 | |
| 	/* Regular file read hook. Call the appropriate callback function to
 | |
| 	 * generate and return the data.
 | |
| 	 */
 | |
| 
 | |
| 	buf_init(off, *len);
 | |
| 
 | |
| 	/* Populate the buffer with the proper content. */
 | |
| 	if (get_inode_index(node) != NO_INDEX) {
 | |
| 		pid_read(node);
 | |
| 	} else {
 | |
| 		((void (*) (void)) cbdata)();
 | |
| 	}
 | |
| 
 | |
| 	*len = buf_get(ptr);
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				rdlink_hook				     *
 | |
|  *===========================================================================*/
 | |
| int rdlink_hook(struct inode *node, char *ptr, size_t max,
 | |
| 	cbdata_t UNUSED(cbdata))
 | |
| {
 | |
| 	/* Symbolic link resolution hook. Not used yet.
 | |
| 	 */
 | |
| 	struct inode *parent;
 | |
| 
 | |
| 	/* Get the parent inode. */
 | |
| 	parent = get_parent_inode(node);
 | |
| 
 | |
| 	/* If the parent inode is a pid directory, call the pid handler.
 | |
| 	 */
 | |
| 	if (parent != NULL && dir_is_pid(parent))
 | |
| 		pid_link(node, ptr, max);
 | |
| 
 | |
| 	return OK;
 | |
| }
 | 
