1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "compatibility.h"
7
8#include "partition_support.h"
9
10#include <errno.h>
11#include <unistd.h>
12
13#include <list>
14
15#include "fssh_errno.h"
16#include "fssh_stat.h"
17#include "fssh_unistd.h"
18#include "stat_priv.h"
19
20
21using namespace FSShell;
22
23
24namespace FSShell {
25
26
27struct FileRestriction {
28	FileRestriction(fssh_dev_t device, fssh_ino_t node, fssh_off_t startOffset,
29			fssh_off_t endOffset)
30		:
31		device(device),
32		node(node),
33		startOffset(startOffset),
34		endOffset(endOffset)
35	{
36	}
37
38	fssh_dev_t	device;
39	fssh_ino_t	node;
40	fssh_off_t	startOffset;
41	fssh_off_t	endOffset;
42};
43
44
45typedef std::list<FileRestriction*> FileRestrictionList;
46
47static FileRestrictionList sFileRestrictions;
48
49
50static FileRestriction*
51find_file_restriction(fssh_dev_t device, fssh_ino_t node)
52{
53	for (FileRestrictionList::iterator it = sFileRestrictions.begin();
54			it != sFileRestrictions.end(); ++it) {
55		FileRestriction* restriction = *it;
56		if (restriction->device == device && restriction->node == node)
57			return restriction;
58	}
59
60	return NULL;
61}
62
63
64static FileRestriction*
65find_file_restriction(int fd)
66{
67	struct fssh_stat st;
68	if (unrestricted_fstat(fd, &st) < 0)
69		return NULL;
70
71	return find_file_restriction(st.fssh_st_dev, st.fssh_st_ino);
72}
73
74
75void
76add_file_restriction(const char* fileName, fssh_off_t startOffset,
77	fssh_off_t endOffset)
78{
79	struct fssh_stat st;
80	if (unrestricted_stat(fileName, &st) < 0)
81		return;
82
83	fssh_dev_t device = st.fssh_st_dev;
84	fssh_ino_t node = st.fssh_st_ino;
85
86	FileRestriction* restriction = find_file_restriction(device, node);
87	if (restriction)
88		return;
89
90	if (endOffset < 0)
91		endOffset = st.fssh_st_size;
92
93	restriction = new FileRestriction(device, node, startOffset, endOffset);
94	sFileRestrictions.push_back(restriction);
95}
96
97
98void
99restricted_file_opened(int fd)
100{
101	FileRestriction* restriction = find_file_restriction(fd);
102	if (!restriction)
103		return;
104
105	lseek(fd, restriction->startOffset, SEEK_SET);
106}
107
108
109void
110restricted_file_duped(int oldFD, int newFD)
111{
112}
113
114
115void
116restricted_file_closed(int fd)
117{
118}
119
120
121int
122restricted_file_restrict_io(int fd, fssh_off_t& pos, fssh_off_t size)
123{
124	FileRestriction* restriction = find_file_restriction(fd);
125	if (!restriction)
126		return 0;
127
128	if (pos < 0) {
129		pos = lseek(fd, 0, SEEK_CUR);
130		if (pos < 0)
131			return -1;
132	} else
133		pos += restriction->startOffset;
134
135	if (pos < restriction->startOffset || pos > restriction->endOffset) {
136		fssh_set_errno(B_BAD_VALUE);
137		return -1;
138	}
139
140	fssh_off_t maxSize = restriction->endOffset - pos;
141	if (size > maxSize)
142		size = maxSize;
143
144	return 0;
145}
146
147
148void
149restricted_file_restrict_stat(struct fssh_stat* st)
150{
151	FileRestriction* restriction = find_file_restriction(st->fssh_st_dev,
152		st->fssh_st_ino);
153	if (!restriction)
154		return;
155
156	st->fssh_st_size = restriction->endOffset - restriction->startOffset;
157}
158
159
160static int
161to_platform_seek_mode(int fsshWhence)
162{
163	switch (fsshWhence) {
164		case FSSH_SEEK_CUR:
165			return SEEK_CUR;
166		case FSSH_SEEK_END:
167			return SEEK_END;
168		case FSSH_SEEK_SET:
169		default:
170			return SEEK_SET;
171	}
172}
173
174
175}	// namespace FSShell
176
177
178fssh_off_t
179fssh_lseek(int fd, fssh_off_t offset, int whence)
180{
181	FileRestriction* restriction = find_file_restriction(fd);
182	if (!restriction)
183		return lseek(fd, offset, to_platform_seek_mode(whence));
184
185	fssh_off_t pos;
186
187	switch (whence) {
188		case FSSH_SEEK_CUR:
189		{
190			pos = lseek(fd, 0, SEEK_CUR);
191			if (pos < 0)
192				return pos;
193			pos += offset;
194			break;
195		}
196		case FSSH_SEEK_END:
197			pos = restriction->endOffset + offset;
198			break;
199		case FSSH_SEEK_SET:
200		default:
201			pos = restriction->startOffset + offset;
202			break;
203	}
204
205	if (pos < restriction->startOffset) {
206		fssh_set_errno(B_BAD_VALUE);
207		return -1;
208	}
209
210	pos = lseek(fd, pos, SEEK_SET);
211	if (pos >= 0)
212		pos -= restriction->startOffset;
213
214	return pos;
215}
216