1/* $OpenBSD: fuse_subr.c,v 1.12 2018/05/21 11:47:46 helg Exp $ */
2/*
3 * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <errno.h>
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22
23#include "fuse_private.h"
24#include "debug.h"
25
26struct fuse_vnode *
27alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t pino)
28{
29	struct fuse_vnode *vn;
30
31	if ((vn = malloc(sizeof(*vn))) == NULL) {
32		DPERROR(__func__);
33		return (NULL);
34	}
35
36	vn->ino = ino;
37	vn->ref = 1;
38	if (strlcpy(vn->path, path, sizeof(vn->path)) >= sizeof(vn->path)) {
39		DPRINTF("%s: strlcpy name too long\n", __func__);
40		free(vn);
41		return (NULL);
42	}
43
44	if (pino == (ino_t)0)
45		vn->parent = NULL;
46	else {
47		if ((vn->parent = tree_get(&f->vnode_tree, pino)) == NULL) {
48			DPRINTF("%s: parent vnode %llu not in the vnode tree\n",
49			    __func__, pino);
50			free(vn);
51			errno = ENOENT;
52			return (NULL);
53		}
54		ref_vn(vn->parent);
55	}
56
57	if (ino == (ino_t)-1) {
58		f->max_ino++;
59		vn->ino = f->max_ino;
60		DPRINTF("New Inode: %llu\t", (unsigned long long)vn->ino);
61	}
62
63	return (vn);
64}
65
66void
67ref_vn(struct fuse_vnode *vn)
68{
69	vn->ref++;
70}
71
72void
73unref_vn(struct fuse *f, struct fuse_vnode *vn)
74{
75	if (--vn->ref == 0) {
76		tree_pop(&f->vnode_tree, vn->ino);
77		remove_vnode_from_name_tree(f, vn);
78		if (vn->parent != NULL)
79			unref_vn(f, vn->parent);
80		free(vn);
81	}
82}
83
84int
85set_vn(struct fuse *f, struct fuse_vnode *v)
86{
87	struct fuse_vn_head *vn_head;
88	struct fuse_vnode *vn;
89
90	if (tree_set(&f->vnode_tree, v->ino, v) == NULL)
91		return (0);
92
93	if (!dict_check(&f->name_tree, v->path)) {
94		vn_head = malloc(sizeof(*vn_head));
95		if (vn_head == NULL)
96			return (0);
97		SIMPLEQ_INIT(vn_head);
98	} else {
99		vn_head = dict_get(&f->name_tree, v->path);
100		if (vn_head == NULL)
101			return (0);
102	}
103
104	SIMPLEQ_FOREACH(vn, vn_head, node) {
105		if (v->parent == vn->parent && v->ino == vn->ino)
106			return (1);
107	}
108
109	SIMPLEQ_INSERT_TAIL(vn_head, v, node);
110	dict_set(&f->name_tree, v->path, vn_head);
111
112	return (1);
113}
114
115void
116remove_vnode_from_name_tree(struct fuse *f, struct fuse_vnode *vn)
117{
118	struct fuse_vn_head *vn_head;
119	struct fuse_vnode *v;
120	struct fuse_vnode *lastv;
121
122	vn_head = dict_get(&f->name_tree, vn->path);
123	if (vn_head == NULL)
124		return;
125
126	lastv = NULL;
127	SIMPLEQ_FOREACH(v, vn_head, node) {
128		if (v->parent == vn->parent)
129			break;
130
131		lastv = v;
132	}
133	if (v == NULL)
134		return;
135
136	/* if we found the vnode remove it */
137	if (v == SIMPLEQ_FIRST(vn_head))
138		SIMPLEQ_REMOVE_HEAD(vn_head, node);
139	else
140		SIMPLEQ_REMOVE_AFTER(vn_head, lastv, node);
141
142	/* if the queue is empty we need to remove it from the dict */
143	if (SIMPLEQ_EMPTY(vn_head)) {
144		vn_head = dict_pop(&f->name_tree, vn->path);
145		free(vn_head);
146	}
147}
148
149struct fuse_vnode *
150get_vn_by_name_and_parent(struct fuse *f, uint8_t *xpath, ino_t pino)
151{
152	struct fuse_vn_head *vn_head;
153	struct fuse_vnode *v = NULL;
154	const char *path = (const char *)xpath;
155
156	vn_head = dict_get(&f->name_tree, path);
157
158	if (vn_head == NULL)
159		goto fail;
160
161	SIMPLEQ_FOREACH(v, vn_head, node)
162		if (v->parent && v->parent->ino == pino)
163			return (v);
164
165fail:
166	errno = ENOENT;
167	return (NULL);
168}
169
170char *
171build_realname(struct fuse *f, ino_t ino)
172{
173	struct fuse_vnode *vn;
174	char *name;
175	char *tmp = NULL;
176	int firstshot = 0, ret;
177
178	name = strdup("/");
179	if (name == NULL)
180		return (NULL);
181
182	vn = tree_get(&f->vnode_tree, ino);
183	if (!vn || !name) {
184		free(name);
185		return (NULL);
186	}
187
188	while (vn->parent != NULL) {
189		if (firstshot++)
190			ret = asprintf(&tmp, "/%s%s", vn->path, name);
191		else
192			ret = asprintf(&tmp, "/%s", vn->path);
193
194		if (ret == -1) {
195			free(name);
196			return (NULL);
197		}
198
199		free(name);
200		name = tmp;
201		tmp = NULL;
202		vn = vn->parent;
203	}
204
205	if (ino == (ino_t)0)
206		DPRINTF("%s: NULL ino\t", __func__);
207
208	DPRINTF("%s", name);
209	return (name);
210}
211