1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
/* Wrapper for diskfs_lookup_hard
Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
Written by Michael I. Bushnell, p/BSG.
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2, or (at
your option) any later version.
The GNU Hurd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
#include "priv.h"
#include <string.h>
static struct
{
int present;
int absent;
int errors;
int dot;
int dotdot;
} cache_misses;
static spin_lock_t cm_lock = SPIN_LOCK_INITIALIZER;
/* Lookup in directory DP (which is locked) the name NAME. TYPE will
either be LOOKUP, CREATE, RENAME, or REMOVE. CRED identifies the
user making the call.
NAME will have leading and trailing slashes stripped. It is an
error if there are internal slashes. NAME will be modified in
place if there are slashes in it; it is therefore an error to
specify a constant NAME which contains slashes.
If the name is found, return zero, and (if NP is nonzero) set *NP
to point to the node for it, locked. If the name is not found,
return ENOENT, and (if NP is nonzero) set *NP to zero. If NP is
zero, then the node found must not be locked, even transitorily.
Lookups for REMOVE and RENAME (which must often check permissions
on the node being found) will always set NP.
If DS is nonzero then:
For LOOKUP: set *DS to be ignored by diskfs_drop_dirstat.
For CREATE: on success, set *DS to be ignored by diskfs_drop_dirstat.
on failure, set *DS for a future call to diskfs_direnter.
For RENAME: on success, set *DS for a future call to diskfs_dirrewrite.
on failure, set *DS for a future call to diskfs_direnter.
For REMOVE: on success, set *DS for a future call to diskfs_dirremove.
on failure, set *DS to be ignored by diskfs_drop_dirstat.
The caller of this function guarantees that if DS is nonzero, then
either the appropriate call listed above or diskfs_drop_dirstat will
be called with DS before the directory DP is unlocked, and guarantees
that no lookup calls will be made on this directory between this
lookup and the use (or descruction) of *DS.
If you use the library's versions of diskfs_rename_dir,
diskfs_clear_directory, and diskfs_init_dir, then lookups for `..'
might have the flag SPEC_DOTDOT or'd in. This has the following special
meaning:
For LOOKUP: DP should be unlocked and its reference dropped before
returning.
For RENAME and REMOVE: The node being found (*NP) is already held
locked, so don't lock it or add a reference to it.
(SPEC_DOTDOT will not be given with CREATE.)
Return ENOTDIR if DP is not a directory.
Return EACCES if CRED isn't allowed to search DP.
Return EACCES if completing the operation will require writing
the directory and diskfs_checkdirmod won't allow the modification.
Return ENOENT if NAME isn't in the directory.
Return EAGAIN if NAME refers to the `..' of this filesystem's root.
Return EIO if appropriate.
This function is a wrapper for diskfs_lookup_hard. */
error_t
diskfs_lookup (struct node *dp, char *name, enum lookup_type type,
struct node **np, struct dirstat *ds, struct protid *cred)
{
error_t err;
struct node *cached;
if (type == REMOVE || type == RENAME)
assert (np);
if (!S_ISDIR (dp->dn_stat.st_mode))
{
if (ds)
diskfs_null_dirstat (ds);
return ENOTDIR;
}
/* Strip leading and trailing slashes. */
while (*name == '/')
name++;
if (name[0] == '\0')
{
if (ds)
diskfs_null_dirstat (ds);
return EINVAL;
}
else
{
char *p = strchr (name, '/');
if (p != 0)
{
*p = '\0';
do
++p;
while (*p == '/');
if (*p != '\0')
{
if (ds)
diskfs_null_dirstat (ds);
return EINVAL;
}
}
}
err = fshelp_access (&dp->dn_stat, S_IEXEC, cred->user);
if (err)
{
if (ds)
diskfs_null_dirstat (ds);
return err;
}
if (dp == cred->po->shadow_root
&& name[0] == '.' && name[1] == '.' && name[2] == '\0')
/* Ran into the root. */
{
if (ds)
diskfs_null_dirstat (ds);
return EAGAIN;
}
if (type == LOOKUP)
/* Check the cache first */
cached = diskfs_check_lookup_cache (dp, name);
else
cached = 0;
if (cached == (struct node *)-1)
/* Negative lookup cached. */
{
if (np)
*np = 0;
return ENOENT;
}
else if (cached)
{
if (np)
*np = cached; /* Return what we found. */
else
/* Ick, the user doesn't want the result, we have to drop our
reference. */
if (cached == dp)
diskfs_nrele (cached);
else
diskfs_nput (cached);
if (ds)
diskfs_null_dirstat (ds);
}
else
{
err = diskfs_lookup_hard (dp, name, type, np, ds, cred);
spin_lock (&cm_lock);
if (type == LOOKUP)
{
if (err == ENOENT)
cache_misses.absent++;
else if (err)
cache_misses.errors++;
else
cache_misses.present++;
if (name[0] == '.')
{
if (name[1] == '\0')
cache_misses.dot++;
else if (name[1] == '.' && name[2] == '\0')
cache_misses.dotdot++;
}
}
spin_unlock (&cm_lock);
if (err && err != ENOENT)
return err;
if (type == RENAME
|| (type == CREATE && err == ENOENT)
|| (type == REMOVE && err != ENOENT))
{
error_t err2;
if (diskfs_name_max > 0 && strlen (name) > diskfs_name_max)
err2 = ENAMETOOLONG;
else
err2 = fshelp_checkdirmod (&dp->dn_stat,
(err || !np) ? 0 : &(*np)->dn_stat,
cred->user);
if (err2)
{
if (np && !err)
{
if (*np == dp)
diskfs_nrele (*np);
else
diskfs_nput (*np);
*np = 0;
}
return err2;
}
}
if ((type == LOOKUP || type == CREATE) && !err && np)
diskfs_enter_lookup_cache (dp, *np, name);
else if (type == LOOKUP && err == ENOENT)
diskfs_enter_lookup_cache (dp, 0, name);
}
return err;
}
|