]>
Commit | Line | Data |
---|---|---|
12f61e09 | 1 | /* |
cd68afe3 | 2 | Unix SMB/CIFS implementation. |
0e8fd339 | 3 | Directory handling routines |
55f400bd | 4 | Copyright (C) Andrew Tridgell 1992-1998 |
12f61e09 JA |
5 | Copyright (C) Jeremy Allison 2007 |
6 | ||
0e8fd339 SRA |
7 | This program is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
d824b98f | 9 | the Free Software Foundation; either version 3 of the License, or |
0e8fd339 | 10 | (at your option) any later version. |
12f61e09 | 11 | |
0e8fd339 SRA |
12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
12f61e09 | 16 | |
0e8fd339 | 17 | You should have received a copy of the GNU General Public License |
5e54558c | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
0e8fd339 SRA |
19 | */ |
20 | ||
21 | #include "includes.h" | |
0e771263 | 22 | #include "system/filesys.h" |
15e9e294 | 23 | #include "locking/share_mode_lock.h" |
8c24ebf3 | 24 | #include "smbd/smbd.h" |
3dde0cbb | 25 | #include "smbd/globals.h" |
f768b32e | 26 | #include "libcli/security/security.h" |
6e6aaace | 27 | #include "lib/util/bitmap.h" |
45807028 | 28 | #include "../lib/util/memcache.h" |
035fd720 | 29 | #include "../librpc/gen_ndr/open_files.h" |
c2ac923c | 30 | #include "lib/util/string_wrappers.h" |
8ad55c38 | 31 | #include "libcli/smb/reparse.h" |
0e8fd339 | 32 | |
0e8fd339 SRA |
33 | /* |
34 | This module implements directory related functions for Samba. | |
35 | */ | |
36 | ||
58a3749e JA |
37 | /* "Special" directory offsets. */ |
38 | #define END_OF_DIRECTORY_OFFSET ((long)-1) | |
39 | #define START_OF_DIRECTORY_OFFSET ((long)0) | |
40 | #define DOT_DOT_DIRECTORY_OFFSET ((long)0x80000000) | |
41 | ||
784adfbc JA |
42 | /* Make directory handle internals available. */ |
43 | ||
784adfbc JA |
44 | struct smb_Dir { |
45 | connection_struct *conn; | |
f6e05320 | 46 | DIR *dir; |
852cce99 | 47 | struct smb_filename *dir_smb_fname; |
c3fedee2 | 48 | unsigned int file_number; |
af35c684 | 49 | bool case_sensitive; |
e89ec641 JA |
50 | files_struct *fsp; /* Back pointer to containing fsp, only |
51 | set from OpenDir_fsp(). */ | |
784adfbc JA |
52 | }; |
53 | ||
54 | struct dptr_struct { | |
55 | struct dptr_struct *next, *prev; | |
3db52feb | 56 | int dnum; |
784adfbc JA |
57 | struct connection_struct *conn; |
58 | struct smb_Dir *dir_hnd; | |
784adfbc | 59 | char *wcard; |
6abd9867 | 60 | uint32_t attr; |
30191d1a JA |
61 | bool has_wild; /* Set to true if the wcard entry has MS wildcard characters in it. */ |
62 | bool did_stat; /* Optimisation for non-wcard searches. */ | |
3ddd9916 | 63 | bool priv; /* Directory handle opened with privilege. */ |
97cd9c67 | 64 | uint32_t counter; |
0b271757 | 65 | |
baaa0c66 VL |
66 | char *last_name_sent; /* for name-based trans2 resume */ |
67 | ||
0b271757 VL |
68 | struct { |
69 | char *fname; | |
70 | struct smb_filename *smb_fname; | |
71 | uint32_t mode; | |
72 | } overflow; | |
784adfbc | 73 | }; |
0e8fd339 | 74 | |
afd037df VL |
75 | static NTSTATUS OpenDir_fsp( |
76 | TALLOC_CTX *mem_ctx, | |
77 | connection_struct *conn, | |
78 | files_struct *fsp, | |
79 | const char *mask, | |
80 | uint32_t attr, | |
81 | struct smb_Dir **_dir_hnd); | |
0e8fd339 | 82 | |
79eae9e3 | 83 | static int smb_Dir_destructor(struct smb_Dir *dir_hnd); |
41c94b8b | 84 | |
3db52feb AT |
85 | #define INVALID_DPTR_KEY (-3) |
86 | ||
0e8fd339 | 87 | /**************************************************************************** |
3db52feb | 88 | Initialise the dir bitmap. |
0e8fd339 | 89 | ****************************************************************************/ |
3db52feb | 90 | |
59c3f5e3 | 91 | bool init_dptrs(struct smbd_server_connection *sconn) |
0e8fd339 | 92 | { |
066fecd3 | 93 | if (sconn->searches.dptr_bmap) { |
59c3f5e3 SM |
94 | return true; |
95 | } | |
3db52feb | 96 | |
066fecd3 | 97 | sconn->searches.dptr_bmap = bitmap_talloc( |
c67e498a | 98 | sconn, MAX_DIRECTORY_HANDLES); |
59c3f5e3 | 99 | |
066fecd3 | 100 | if (sconn->searches.dptr_bmap == NULL) { |
59c3f5e3 SM |
101 | return false; |
102 | } | |
3db52feb | 103 | |
59c3f5e3 | 104 | return true; |
0e8fd339 SRA |
105 | } |
106 | ||
0e8fd339 | 107 | /**************************************************************************** |
784adfbc | 108 | Get the struct dptr_struct for a dir index. |
0e8fd339 | 109 | ****************************************************************************/ |
3db52feb | 110 | |
59c3f5e3 | 111 | static struct dptr_struct *dptr_get(struct smbd_server_connection *sconn, |
ff8c8b34 | 112 | int key) |
0e8fd339 | 113 | { |
784adfbc | 114 | struct dptr_struct *dptr; |
20f1cf6c | 115 | |
b97d18f0 SS |
116 | for (dptr = sconn->searches.dirptrs; dptr != NULL; dptr = dptr->next) { |
117 | if(dptr->dnum != key) { | |
118 | continue; | |
119 | } | |
b97d18f0 SS |
120 | DLIST_PROMOTE(sconn->searches.dirptrs, dptr); |
121 | return dptr; | |
20f1cf6c JA |
122 | } |
123 | return(NULL); | |
0e8fd339 SRA |
124 | } |
125 | ||
3db52feb AT |
126 | /**************************************************************************** |
127 | Get the dir path for a dir index. | |
128 | ****************************************************************************/ | |
129 | ||
e54cf153 | 130 | const char *dptr_path(struct smbd_server_connection *sconn, int key) |
0e8fd339 | 131 | { |
ff8c8b34 | 132 | struct dptr_struct *dptr = dptr_get(sconn, key); |
20f1cf6c | 133 | if (dptr) |
a1d348fd | 134 | return(dptr->dir_hnd->dir_smb_fname->base_name); |
20f1cf6c | 135 | return(NULL); |
0e8fd339 SRA |
136 | } |
137 | ||
138 | /**************************************************************************** | |
784adfbc | 139 | Get the dir wcard for a dir index. |
0e8fd339 | 140 | ****************************************************************************/ |
3db52feb | 141 | |
e54cf153 | 142 | const char *dptr_wcard(struct smbd_server_connection *sconn, int key) |
0e8fd339 | 143 | { |
ff8c8b34 | 144 | struct dptr_struct *dptr = dptr_get(sconn, key); |
20f1cf6c JA |
145 | if (dptr) |
146 | return(dptr->wcard); | |
147 | return(NULL); | |
0e8fd339 SRA |
148 | } |
149 | ||
150 | /**************************************************************************** | |
784adfbc | 151 | Get the dir attrib for a dir index. |
0e8fd339 | 152 | ****************************************************************************/ |
3db52feb | 153 | |
6abd9867 | 154 | uint16_t dptr_attr(struct smbd_server_connection *sconn, int key) |
0e8fd339 | 155 | { |
ff8c8b34 | 156 | struct dptr_struct *dptr = dptr_get(sconn, key); |
784adfbc JA |
157 | if (dptr) |
158 | return(dptr->attr); | |
159 | return(0); | |
0e8fd339 SRA |
160 | } |
161 | ||
0e8fd339 | 162 | /**************************************************************************** |
3db52feb | 163 | Close all dptrs for a cnum. |
0e8fd339 | 164 | ****************************************************************************/ |
3db52feb | 165 | |
b9623ab5 | 166 | void dptr_closecnum(connection_struct *conn) |
0e8fd339 | 167 | { |
784adfbc | 168 | struct dptr_struct *dptr, *next; |
59c3f5e3 SM |
169 | struct smbd_server_connection *sconn = conn->sconn; |
170 | ||
171 | if (sconn == NULL) { | |
172 | return; | |
173 | } | |
174 | ||
066fecd3 | 175 | for(dptr = sconn->searches.dirptrs; dptr; dptr = next) { |
20f1cf6c | 176 | next = dptr->next; |
59c3f5e3 | 177 | if (dptr->conn == conn) { |
f5bc73a2 VL |
178 | /* |
179 | * Need to make a copy, "dptr" will be gone | |
180 | * after close_file_free() returns | |
181 | */ | |
182 | struct files_struct *fsp = dptr->dir_hnd->fsp; | |
183 | close_file_free(NULL, &fsp, NORMAL_CLOSE); | |
59c3f5e3 | 184 | } |
20f1cf6c | 185 | } |
0e8fd339 SRA |
186 | } |
187 | ||
0e8fd339 | 188 | /**************************************************************************** |
3db52feb AT |
189 | Create a new dir ptr. If the flag old_handle is true then we must allocate |
190 | from the bitmap range 0 - 255 as old SMBsearch directory handles are only | |
191 | one byte long. If old_handle is false we allocate from the range | |
192 | 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure | |
784adfbc | 193 | a directory handle is never zero. |
15b842d4 | 194 | wcard must not be zero. |
0e8fd339 | 195 | ****************************************************************************/ |
3db52feb | 196 | |
1c2aacd6 JA |
197 | NTSTATUS dptr_create(connection_struct *conn, |
198 | struct smb_request *req, | |
199 | files_struct *fsp, | |
247481c1 | 200 | bool old_handle, |
247481c1 | 201 | const char *wcard, |
247481c1 JA |
202 | uint32_t attr, |
203 | struct dptr_struct **dptr_ret) | |
0e8fd339 | 204 | { |
59c3f5e3 | 205 | struct smbd_server_connection *sconn = conn->sconn; |
784adfbc | 206 | struct dptr_struct *dptr = NULL; |
2cef24a5 | 207 | struct smb_Dir *dir_hnd = NULL; |
afd037df | 208 | NTSTATUS status; |
784adfbc | 209 | |
6d0924d8 | 210 | DBG_INFO("dir=%s\n", fsp_str_dbg(fsp)); |
0e8fd339 | 211 | |
59c3f5e3 SM |
212 | if (sconn == NULL) { |
213 | DEBUG(0,("dptr_create: called with fake connection_struct\n")); | |
214 | return NT_STATUS_INTERNAL_ERROR; | |
215 | } | |
216 | ||
15b842d4 | 217 | if (!wcard) { |
83eb0d1d | 218 | return NT_STATUS_INVALID_PARAMETER; |
15b842d4 JA |
219 | } |
220 | ||
d485c43c JA |
221 | if (!(fsp->access_mask & SEC_DIR_LIST)) { |
222 | DBG_INFO("dptr_create: directory %s " | |
223 | "not open for LIST access\n", | |
6d0924d8 | 224 | fsp_str_dbg(fsp)); |
d485c43c | 225 | return NT_STATUS_ACCESS_DENIED; |
83eb0d1d | 226 | } |
afd037df VL |
227 | status = OpenDir_fsp(NULL, conn, fsp, wcard, attr, &dir_hnd); |
228 | if (!NT_STATUS_IS_OK(status)) { | |
229 | return status; | |
784adfbc JA |
230 | } |
231 | ||
9e810b2f | 232 | dptr = talloc_zero(NULL, struct dptr_struct); |
20f1cf6c | 233 | if(!dptr) { |
872fb6a3 | 234 | DEBUG(0,("talloc fail in dptr_create.\n")); |
ec412b60 | 235 | TALLOC_FREE(dir_hnd); |
83eb0d1d | 236 | return NT_STATUS_NO_MEMORY; |
20f1cf6c | 237 | } |
3db52feb | 238 | |
39bb5a62 SM |
239 | dptr->conn = conn; |
240 | dptr->dir_hnd = dir_hnd; | |
872fb6a3 | 241 | dptr->wcard = talloc_strdup(dptr, wcard); |
39bb5a62 | 242 | if (!dptr->wcard) { |
872fb6a3 | 243 | TALLOC_FREE(dptr); |
39bb5a62 SM |
244 | TALLOC_FREE(dir_hnd); |
245 | return NT_STATUS_NO_MEMORY; | |
246 | } | |
6e856074 | 247 | if ((req != NULL && req->posix_pathnames) || ISDOT(wcard)) { |
39bb5a62 SM |
248 | dptr->has_wild = True; |
249 | } else { | |
33fffcd2 | 250 | dptr->has_wild = ms_has_wild(dptr->wcard); |
39bb5a62 SM |
251 | } |
252 | ||
253 | dptr->attr = attr; | |
254 | ||
22ddbb50 SM |
255 | if (sconn->using_smb2) { |
256 | goto done; | |
257 | } | |
258 | ||
20f1cf6c | 259 | if(old_handle) { |
3db52feb | 260 | |
20f1cf6c JA |
261 | /* |
262 | * This is an old-style SMBsearch request. Ensure the | |
263 | * value we return will fit in the range 1-255. | |
264 | */ | |
3db52feb | 265 | |
066fecd3 | 266 | dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 0); |
3db52feb | 267 | |
20f1cf6c | 268 | if(dptr->dnum == -1 || dptr->dnum > 254) { |
1376dc3b JA |
269 | DBG_ERR("returned %d: Error - all old " |
270 | "dirptrs in use ?\n", | |
271 | dptr->dnum); | |
272 | TALLOC_FREE(dptr); | |
273 | TALLOC_FREE(dir_hnd); | |
274 | return NT_STATUS_TOO_MANY_OPENED_FILES; | |
20f1cf6c JA |
275 | } |
276 | } else { | |
3db52feb | 277 | |
20f1cf6c JA |
278 | /* |
279 | * This is a new-style trans2 request. Allocate from | |
280 | * a range that will return 256 - MAX_DIRECTORY_HANDLES. | |
281 | */ | |
3db52feb | 282 | |
066fecd3 | 283 | dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 255); |
3db52feb | 284 | |
20f1cf6c | 285 | if(dptr->dnum == -1 || dptr->dnum < 255) { |
6134922f JA |
286 | DBG_ERR("returned %d: Error - all new " |
287 | "dirptrs in use ?\n", | |
288 | dptr->dnum); | |
289 | TALLOC_FREE(dptr); | |
290 | TALLOC_FREE(dir_hnd); | |
291 | return NT_STATUS_TOO_MANY_OPENED_FILES; | |
20f1cf6c JA |
292 | } |
293 | } | |
0e8fd339 | 294 | |
066fecd3 | 295 | bitmap_set(sconn->searches.dptr_bmap, dptr->dnum); |
0e8fd339 | 296 | |
20f1cf6c | 297 | dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */ |
0e8fd339 | 298 | |
066fecd3 | 299 | DLIST_ADD(sconn->searches.dirptrs, dptr); |
0e8fd339 | 300 | |
22ddbb50 | 301 | done: |
1a040c7f VL |
302 | DBG_INFO("creating new dirptr [%d] for path [%s]\n", |
303 | dptr->dnum, fsp_str_dbg(fsp)); | |
0e8fd339 | 304 | |
fd37f981 | 305 | *dptr_ret = dptr; |
784adfbc | 306 | |
83eb0d1d | 307 | return NT_STATUS_OK; |
0e8fd339 SRA |
308 | } |
309 | ||
784adfbc JA |
310 | |
311 | /**************************************************************************** | |
312 | Wrapper functions to access the lower level directory handles. | |
313 | ****************************************************************************/ | |
314 | ||
224fc03c | 315 | void dptr_CloseDir(files_struct *fsp) |
784adfbc | 316 | { |
f1b749ee | 317 | struct smbd_server_connection *sconn = NULL; |
3e0cafc2 | 318 | |
7d72cf02 RB |
319 | if (fsp->dptr == NULL) { |
320 | return; | |
224fc03c | 321 | } |
f1b749ee | 322 | sconn = fsp->dptr->conn->sconn; |
7d72cf02 RB |
323 | |
324 | /* | |
325 | * The destructor for the struct smb_Dir (fsp->dptr->dir_hnd) | |
326 | * now handles all resource deallocation. | |
327 | */ | |
328 | ||
3e0cafc2 RB |
329 | DBG_INFO("closing dptr key %d\n", fsp->dptr->dnum); |
330 | ||
331 | if (sconn != NULL && !sconn->using_smb2) { | |
332 | DLIST_REMOVE(sconn->searches.dirptrs, fsp->dptr); | |
333 | ||
334 | /* | |
335 | * Free the dnum in the bitmap. Remember the dnum value is | |
336 | * always biased by one with respect to the bitmap. | |
337 | */ | |
338 | ||
339 | if (!bitmap_query(sconn->searches.dptr_bmap, | |
340 | fsp->dptr->dnum - 1)) | |
341 | { | |
342 | DBG_ERR("closing dnum = %d and bitmap not set !\n", | |
343 | fsp->dptr->dnum); | |
344 | } | |
345 | ||
346 | bitmap_clear(sconn->searches.dptr_bmap, fsp->dptr->dnum - 1); | |
347 | } | |
348 | ||
349 | TALLOC_FREE(fsp->dptr->dir_hnd); | |
350 | TALLOC_FREE(fsp->dptr); | |
784adfbc JA |
351 | } |
352 | ||
e4631270 VL |
353 | void dptr_RewindDir(struct dptr_struct *dptr) |
354 | { | |
e2fbe361 | 355 | RewindDir(dptr->dir_hnd); |
91c76f65 | 356 | dptr->did_stat = false; |
0b271757 VL |
357 | TALLOC_FREE(dptr->overflow.fname); |
358 | TALLOC_FREE(dptr->overflow.smb_fname); | |
e4631270 VL |
359 | } |
360 | ||
d14b1e2d VL |
361 | unsigned int dptr_FileNumber(struct dptr_struct *dptr) |
362 | { | |
363 | return dptr->dir_hnd->file_number; | |
364 | } | |
365 | ||
30191d1a | 366 | bool dptr_has_wild(struct dptr_struct *dptr) |
315ad641 JA |
367 | { |
368 | return dptr->has_wild; | |
369 | } | |
370 | ||
fd37f981 JA |
371 | int dptr_dnum(struct dptr_struct *dptr) |
372 | { | |
373 | return dptr->dnum; | |
374 | } | |
375 | ||
3ddd9916 JA |
376 | bool dptr_get_priv(struct dptr_struct *dptr) |
377 | { | |
378 | return dptr->priv; | |
379 | } | |
380 | ||
381 | void dptr_set_priv(struct dptr_struct *dptr) | |
382 | { | |
383 | dptr->priv = true; | |
384 | } | |
385 | ||
e163f22e JA |
386 | bool dptr_case_sensitive(struct dptr_struct *dptr) |
387 | { | |
388 | return dptr->dir_hnd->case_sensitive; | |
389 | } | |
390 | ||
021011f9 JA |
391 | /**************************************************************************** |
392 | Return the next visible file name, skipping veto'd and invisible files. | |
393 | ****************************************************************************/ | |
394 | ||
f36bdcc0 | 395 | char *dptr_ReadDirName(TALLOC_CTX *ctx, struct dptr_struct *dptr) |
021011f9 | 396 | { |
f36bdcc0 VL |
397 | struct stat_ex st = { |
398 | .st_ex_nlink = 0, | |
399 | }; | |
91c76f65 VL |
400 | struct smb_Dir *dir_hnd = dptr->dir_hnd; |
401 | struct files_struct *dir_fsp = dir_hnd->fsp; | |
402 | struct smb_filename *dir_name = dir_fsp->fsp_name; | |
d1c34d40 | 403 | struct smb_filename smb_fname_base; |
91c76f65 VL |
404 | bool retry_scanning = false; |
405 | int ret; | |
406 | int flags = 0; | |
69d61453 | 407 | |
91c76f65 VL |
408 | if (dptr->has_wild) { |
409 | const char *name_temp = NULL; | |
410 | char *talloced = NULL; | |
daf6f2f7 | 411 | name_temp = ReadDirName(dir_hnd, &talloced); |
f6650f5d VL |
412 | if (name_temp == NULL) { |
413 | return NULL; | |
414 | } | |
415 | if (talloced != NULL) { | |
416 | return talloc_move(ctx, &talloced); | |
417 | } | |
418 | return talloc_strdup(ctx, name_temp); | |
021011f9 JA |
419 | } |
420 | ||
91c76f65 VL |
421 | if (dptr->did_stat) { |
422 | /* | |
423 | * No wildcard, this is not a real directory traverse | |
424 | * but a "stat" call behind a query_directory. We've | |
425 | * been here, nothing else to look at. | |
426 | */ | |
5713c65a JA |
427 | return NULL; |
428 | } | |
25d345eb SD |
429 | dptr->did_stat = true; |
430 | ||
e129384d | 431 | /* Create an smb_filename with stream_name == NULL. */ |
91c76f65 VL |
432 | smb_fname_base = (struct smb_filename){ |
433 | .base_name = dptr->wcard, | |
434 | .flags = dir_name->flags, | |
435 | .twrp = dir_name->twrp, | |
e9a6552d | 436 | }; |
e129384d | 437 | |
91c76f65 VL |
438 | if (dir_name->flags & SMB_FILENAME_POSIX_PATH) { |
439 | flags |= AT_SYMLINK_NOFOLLOW; | |
440 | } | |
441 | ||
f36bdcc0 | 442 | ret = SMB_VFS_FSTATAT(dptr->conn, dir_fsp, &smb_fname_base, &st, flags); |
91c76f65 VL |
443 | if (ret == 0) { |
444 | return talloc_strdup(ctx, dptr->wcard); | |
25d345eb | 445 | } |
315ad641 | 446 | |
91c76f65 VL |
447 | /* |
448 | * If we get any other error than ENOENT or ENOTDIR | |
449 | * then the file exists, we just can't stat it. | |
25d345eb | 450 | */ |
91c76f65 VL |
451 | if (errno != ENOENT && errno != ENOTDIR) { |
452 | return talloc_strdup(ctx, dptr->wcard); | |
25d345eb | 453 | } |
12f61e09 | 454 | |
69d61453 | 455 | /* |
91c76f65 VL |
456 | * A scan will find the long version of a mangled name as |
457 | * wildcard. | |
458 | */ | |
459 | retry_scanning |= mangle_is_mangled(dptr->wcard, dptr->conn->params); | |
460 | ||
461 | /* | |
462 | * Also retry scanning if the client requested case | |
463 | * insensitive semantics and the file system does not provide | |
464 | * it. | |
69d61453 | 465 | */ |
91c76f65 VL |
466 | retry_scanning |= |
467 | (!dir_hnd->case_sensitive && | |
468 | (dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)); | |
469 | ||
470 | if (retry_scanning) { | |
471 | char *found_name = NULL; | |
472 | NTSTATUS status; | |
473 | ||
474 | status = get_real_filename_at(dir_fsp, | |
89bffa14 VL |
475 | dptr->wcard, |
476 | ctx, | |
477 | &found_name); | |
91c76f65 VL |
478 | if (NT_STATUS_IS_OK(status)) { |
479 | return found_name; | |
480 | } | |
69d61453 | 481 | } |
482 | ||
91c76f65 | 483 | return NULL; |
021011f9 JA |
484 | } |
485 | ||
21859695 RB |
486 | struct files_struct *dir_hnd_fetch_fsp(struct smb_Dir *dir_hnd) |
487 | { | |
488 | return dir_hnd->fsp; | |
489 | } | |
490 | ||
b922f6e7 JA |
491 | /**************************************************************************** |
492 | Fetch the fsp associated with the dptr_num. | |
493 | ****************************************************************************/ | |
494 | ||
495 | files_struct *dptr_fetch_lanman2_fsp(struct smbd_server_connection *sconn, | |
496 | int dptr_num) | |
497 | { | |
b4431ad3 | 498 | struct dptr_struct *dptr = dptr_get(sconn, dptr_num); |
b922f6e7 JA |
499 | if (dptr == NULL) { |
500 | return NULL; | |
501 | } | |
b4431ad3 | 502 | DBG_NOTICE("fetching dirptr %d for path %s\n", |
a1d348fd VL |
503 | dptr_num, |
504 | dptr->dir_hnd->dir_smb_fname->base_name); | |
b922f6e7 JA |
505 | return dptr->dir_hnd->fsp; |
506 | } | |
507 | ||
daa71c42 SM |
508 | bool smbd_dirptr_get_entry(TALLOC_CTX *ctx, |
509 | struct dptr_struct *dirptr, | |
510 | const char *mask, | |
511 | uint32_t dirtype, | |
512 | bool dont_descend, | |
513 | bool ask_sharemode, | |
c98d1113 | 514 | bool get_dosmode_in, |
daa71c42 SM |
515 | bool (*match_fn)(TALLOC_CTX *ctx, |
516 | void *private_data, | |
517 | const char *dname, | |
518 | const char *mask, | |
519 | char **_fname), | |
daa71c42 SM |
520 | void *private_data, |
521 | char **_fname, | |
522 | struct smb_filename **_smb_fname, | |
99799bcd | 523 | uint32_t *_mode) |
daa71c42 SM |
524 | { |
525 | connection_struct *conn = dirptr->conn; | |
4935c0b5 | 526 | struct smb_Dir *dir_hnd = dirptr->dir_hnd; |
b4698f3b | 527 | struct smb_filename *dir_fname = dir_hnd->dir_smb_fname; |
88f32b78 | 528 | bool posix = (dir_fname->flags & SMB_FILENAME_POSIX_PATH); |
5991f4e6 | 529 | const bool toplevel = ISDOT(dir_fname->base_name); |
c98d1113 | 530 | NTSTATUS status; |
daa71c42 SM |
531 | |
532 | *_smb_fname = NULL; | |
533 | *_mode = 0; | |
534 | ||
0b271757 VL |
535 | if (dirptr->overflow.smb_fname != NULL) { |
536 | *_fname = talloc_move(ctx, &dirptr->overflow.fname); | |
537 | *_smb_fname = talloc_move(ctx, &dirptr->overflow.smb_fname); | |
538 | *_mode = dirptr->overflow.mode; | |
539 | return true; | |
540 | } | |
541 | ||
290ca547 VL |
542 | if (dont_descend && (dptr_FileNumber(dirptr) >= 2)) { |
543 | /* | |
544 | * . and .. were returned first, we're done showing | |
545 | * the directory as empty. | |
546 | */ | |
547 | return false; | |
548 | } | |
549 | ||
daa71c42 | 550 | while (true) { |
daa71c42 | 551 | char *dname = NULL; |
daa71c42 | 552 | char *fname = NULL; |
c98d1113 | 553 | struct smb_filename *smb_fname = NULL; |
daa71c42 | 554 | uint32_t mode = 0; |
c98d1113 | 555 | bool get_dosmode = get_dosmode_in; |
5991f4e6 | 556 | bool toplevel_dotdot; |
c96010a2 | 557 | bool visible; |
daa71c42 | 558 | bool ok; |
daa71c42 | 559 | |
f36bdcc0 | 560 | dname = dptr_ReadDirName(ctx, dirptr); |
daa71c42 | 561 | |
c86fef84 | 562 | DBG_DEBUG("dir [%s] dirptr [%p] offset [%u] => " |
4935c0b5 | 563 | "dname [%s]\n", |
b4698f3b | 564 | smb_fname_str_dbg(dir_fname), |
c86fef84 VL |
565 | dirptr, |
566 | dir_hnd->file_number, | |
4935c0b5 | 567 | dname ? dname : "(finished)"); |
daa71c42 SM |
568 | |
569 | if (dname == NULL) { | |
570 | return false; | |
571 | } | |
572 | ||
d3161dd1 JA |
573 | if (IS_VETO_PATH(conn, dname)) { |
574 | TALLOC_FREE(dname); | |
575 | continue; | |
576 | } | |
577 | ||
daa71c42 SM |
578 | /* |
579 | * fname may get mangled, dname is never mangled. | |
580 | * Whenever we're accessing the filesystem we use | |
581 | * pathreal which is composed from dname. | |
582 | */ | |
583 | ||
584 | ok = match_fn(ctx, private_data, dname, mask, &fname); | |
585 | if (!ok) { | |
586 | TALLOC_FREE(dname); | |
587 | continue; | |
588 | } | |
589 | ||
5991f4e6 VL |
590 | toplevel_dotdot = toplevel && ISDOTDOT(dname); |
591 | ||
c96010a2 VL |
592 | smb_fname = synthetic_smb_fname(talloc_tos(), |
593 | toplevel_dotdot ? "." : dname, | |
594 | NULL, | |
595 | NULL, | |
596 | dir_fname->twrp, | |
597 | dir_fname->flags); | |
598 | if (smb_fname == NULL) { | |
599 | TALLOC_FREE(dname); | |
600 | return false; | |
601 | } | |
daa71c42 | 602 | |
c96010a2 VL |
603 | /* |
604 | * UCF_POSIX_PATHNAMES to avoid the readdir fallback | |
605 | * if we get raced between readdir and unlink. | |
606 | */ | |
607 | status = openat_pathref_fsp_lcomp(dir_hnd->fsp, | |
608 | smb_fname, | |
609 | UCF_POSIX_PATHNAMES); | |
610 | if (!NT_STATUS_IS_OK(status)) { | |
611 | DBG_DEBUG("Could not open %s: %s\n", | |
612 | dname, | |
613 | nt_errstr(status)); | |
614 | TALLOC_FREE(smb_fname); | |
615 | TALLOC_FREE(fname); | |
616 | TALLOC_FREE(dname); | |
617 | continue; | |
618 | } | |
b3a0d6a1 | 619 | |
c96010a2 VL |
620 | visible = is_visible_fsp(smb_fname->fsp); |
621 | if (!visible) { | |
622 | TALLOC_FREE(smb_fname); | |
623 | TALLOC_FREE(fname); | |
624 | TALLOC_FREE(dname); | |
625 | continue; | |
626 | } | |
b3a0d6a1 | 627 | |
c96010a2 VL |
628 | if (!S_ISLNK(smb_fname->st.st_ex_mode)) { |
629 | if (get_dosmode) { | |
630 | mode = fdos_mode(smb_fname->fsp); | |
631 | smb_fname->st = smb_fname->fsp->fsp_name->st; | |
88f32b78 | 632 | } |
c98d1113 | 633 | |
5991f4e6 | 634 | if (toplevel_dotdot) { |
88f32b78 VL |
635 | /* |
636 | * Ensure posix fileid and sids are hidden | |
637 | */ | |
638 | smb_fname->st.st_ex_ino = 0; | |
639 | smb_fname->st.st_ex_dev = 0; | |
640 | smb_fname->st.st_ex_uid = -1; | |
641 | smb_fname->st.st_ex_gid = -1; | |
642 | } | |
c98d1113 | 643 | |
88f32b78 | 644 | goto done; |
a66b7de7 JA |
645 | } |
646 | ||
c96010a2 VL |
647 | if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) && |
648 | is_msdfs_link(dir_hnd->fsp, smb_fname)) | |
649 | { | |
650 | DBG_INFO("Masquerading msdfs link %s as a directory\n", | |
651 | smb_fname->base_name); | |
88f32b78 | 652 | |
c96010a2 VL |
653 | smb_fname->st.st_ex_mode = (smb_fname->st.st_ex_mode & |
654 | ~S_IFMT) | | |
655 | S_IFDIR; | |
88f32b78 | 656 | |
c96010a2 VL |
657 | mode = dos_mode_msdfs(conn, dname, &smb_fname->st); |
658 | get_dosmode = false; | |
659 | ask_sharemode = false; | |
660 | goto done; | |
661 | } | |
88f32b78 | 662 | |
c96010a2 VL |
663 | if (posix) { |
664 | /* | |
665 | * Posix always wants to see symlinks, | |
666 | * dangling or not. We've done the | |
667 | * openat_pathref_fsp() to fill in | |
668 | * smb_fname->fsp just in case it's not | |
669 | * dangling. | |
670 | */ | |
671 | mode = FILE_ATTRIBUTE_NORMAL; | |
672 | get_dosmode = false; | |
673 | ask_sharemode = false; | |
674 | goto done; | |
675 | } | |
88f32b78 | 676 | |
c96010a2 VL |
677 | if (!lp_follow_symlinks(SNUM(conn))) { |
678 | /* | |
679 | * Hide symlinks not followed | |
680 | */ | |
681 | TALLOC_FREE(smb_fname); | |
88f32b78 | 682 | TALLOC_FREE(fname); |
c96010a2 VL |
683 | TALLOC_FREE(dname); |
684 | continue; | |
685 | } | |
686 | ||
687 | /* | |
688 | * We have to find out if it's a dangling | |
689 | * symlink. Use the fat logic behind | |
690 | * openat_pathref_fsp(). | |
691 | */ | |
692 | ||
693 | { | |
694 | struct files_struct *fsp = smb_fname->fsp; | |
695 | smb_fname_fsp_unlink(smb_fname); | |
696 | fd_close(fsp); | |
697 | file_free(NULL, fsp); | |
698 | } | |
699 | ||
700 | status = openat_pathref_fsp(dir_hnd->fsp, smb_fname); | |
701 | ||
702 | if (!NT_STATUS_IS_OK(status)) { | |
703 | /* | |
704 | * Dangling symlink. Hide. | |
705 | */ | |
2acad276 | 706 | TALLOC_FREE(smb_fname); |
c96010a2 VL |
707 | TALLOC_FREE(fname); |
708 | TALLOC_FREE(dname); | |
88f32b78 | 709 | continue; |
2acad276 JA |
710 | } |
711 | ||
b1e5ed44 VL |
712 | if (get_dosmode) { |
713 | mode = fdos_mode(smb_fname->fsp); | |
c96010a2 | 714 | smb_fname->st = smb_fname->fsp->fsp_name->st; |
c98d1113 RB |
715 | } |
716 | ||
88f32b78 | 717 | done: |
daa71c42 | 718 | |
7d8e22c7 | 719 | if (!dir_check_ftype(mode, dirtype)) { |
16ae3c2e VL |
720 | DBG_INFO("[%s] attribs 0x%" PRIx32 " didn't match " |
721 | "0x%" PRIx32 "\n", | |
722 | fname, | |
723 | mode, | |
724 | dirtype); | |
c98d1113 | 725 | TALLOC_FREE(smb_fname); |
daa71c42 SM |
726 | TALLOC_FREE(dname); |
727 | TALLOC_FREE(fname); | |
daa71c42 SM |
728 | continue; |
729 | } | |
730 | ||
c98d1113 | 731 | if (ask_sharemode && !S_ISDIR(smb_fname->st.st_ex_mode)) { |
daa71c42 SM |
732 | struct timespec write_time_ts; |
733 | struct file_id fileid; | |
734 | ||
735 | fileid = vfs_file_id_from_sbuf(conn, | |
c98d1113 | 736 | &smb_fname->st); |
a65bce4e | 737 | get_file_infos(fileid, 0, NULL, &write_time_ts); |
69691dd0 | 738 | if (!is_omit_timespec(&write_time_ts)) { |
c98d1113 | 739 | update_stat_ex_mtime(&smb_fname->st, |
daa71c42 SM |
740 | write_time_ts); |
741 | } | |
742 | } | |
743 | ||
16ae3c2e VL |
744 | DBG_NOTICE("mask=[%s] found %s fname=%s (%s)\n", |
745 | mask, | |
746 | smb_fname_str_dbg(smb_fname), | |
747 | dname, | |
748 | fname); | |
daa71c42 | 749 | |
daa71c42 SM |
750 | TALLOC_FREE(dname); |
751 | ||
c98d1113 | 752 | *_smb_fname = talloc_move(ctx, &smb_fname); |
daa71c42 | 753 | *_fname = fname; |
daa71c42 | 754 | *_mode = mode; |
daa71c42 SM |
755 | |
756 | return true; | |
757 | } | |
758 | ||
759 | return false; | |
760 | } | |
761 | ||
0b271757 VL |
762 | void smbd_dirptr_push_overflow(struct dptr_struct *dirptr, |
763 | char **_fname, | |
764 | struct smb_filename **_smb_fname, | |
765 | uint32_t mode) | |
766 | { | |
767 | SMB_ASSERT(dirptr->overflow.fname == NULL); | |
768 | SMB_ASSERT(dirptr->overflow.smb_fname == NULL); | |
769 | ||
770 | dirptr->overflow.fname = talloc_move(dirptr, _fname); | |
771 | dirptr->overflow.smb_fname = talloc_move(dirptr, _smb_fname); | |
772 | dirptr->overflow.mode = mode; | |
773 | } | |
774 | ||
baaa0c66 VL |
775 | void smbd_dirptr_set_last_name_sent(struct dptr_struct *dirptr, |
776 | char **_fname) | |
777 | { | |
778 | TALLOC_FREE(dirptr->last_name_sent); | |
779 | dirptr->last_name_sent = talloc_move(dirptr, _fname); | |
780 | } | |
781 | ||
782 | char *smbd_dirptr_get_last_name_sent(struct dptr_struct *dirptr) | |
783 | { | |
784 | return dirptr->last_name_sent; | |
785 | } | |
786 | ||
53877296 JA |
787 | /******************************************************************* |
788 | Check to see if a user can read an fsp . This is only approximate, | |
789 | it is used as part of the "hide unreadable" option. Don't | |
790 | use it for anything security sensitive. | |
791 | ********************************************************************/ | |
792 | ||
793 | static bool user_can_read_fsp(struct files_struct *fsp) | |
794 | { | |
795 | NTSTATUS status; | |
796 | uint32_t rejected_share_access = 0; | |
797 | uint32_t rejected_mask = 0; | |
798 | struct security_descriptor *sd = NULL; | |
799 | uint32_t access_mask = FILE_READ_DATA| | |
800 | FILE_READ_EA| | |
801 | FILE_READ_ATTRIBUTES| | |
802 | SEC_STD_READ_CONTROL; | |
803 | ||
804 | /* | |
805 | * Never hide files from the root user. | |
806 | * We use (uid_t)0 here not sec_initial_uid() | |
807 | * as make test uses a single user context. | |
808 | */ | |
809 | ||
810 | if (get_current_uid(fsp->conn) == (uid_t)0) { | |
811 | return true; | |
812 | } | |
813 | ||
814 | /* | |
815 | * We can't directly use smbd_check_access_rights_fsp() | |
816 | * here, as this implicitly grants FILE_READ_ATTRIBUTES | |
817 | * which the Windows access-based-enumeration code | |
818 | * explicitly checks for on the file security descriptor. | |
819 | * See bug: | |
820 | * | |
821 | * https://bugzilla.samba.org/show_bug.cgi?id=10252 | |
822 | * | |
823 | * and the smb2.acl2.ACCESSBASED test for details. | |
824 | */ | |
825 | ||
826 | rejected_share_access = access_mask & ~(fsp->conn->share_access); | |
827 | if (rejected_share_access) { | |
828 | DBG_DEBUG("rejected share access 0x%x " | |
829 | "on %s (0x%x)\n", | |
830 | (unsigned int)access_mask, | |
831 | fsp_str_dbg(fsp), | |
832 | (unsigned int)rejected_share_access); | |
833 | return false; | |
834 | } | |
835 | ||
c949e4b2 | 836 | status = SMB_VFS_FGET_NT_ACL(metadata_fsp(fsp), |
53877296 JA |
837 | (SECINFO_OWNER | |
838 | SECINFO_GROUP | | |
839 | SECINFO_DACL), | |
840 | talloc_tos(), | |
841 | &sd); | |
842 | ||
843 | if (!NT_STATUS_IS_OK(status)) { | |
844 | DBG_DEBUG("Could not get acl " | |
845 | "on %s: %s\n", | |
846 | fsp_str_dbg(fsp), | |
847 | nt_errstr(status)); | |
848 | return false; | |
849 | } | |
850 | ||
851 | status = se_file_access_check(sd, | |
852 | get_current_nttok(fsp->conn), | |
853 | false, | |
854 | access_mask, | |
855 | &rejected_mask); | |
856 | ||
857 | TALLOC_FREE(sd); | |
858 | ||
859 | if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { | |
860 | DBG_DEBUG("rejected bits 0x%x read access for %s\n", | |
861 | (unsigned int)rejected_mask, | |
862 | fsp_str_dbg(fsp)); | |
863 | return false; | |
864 | } | |
865 | return true; | |
866 | } | |
867 | ||
c064758f JA |
868 | /******************************************************************* |
869 | Check to see if a user can write to an fsp. | |
870 | Always return true for directories. | |
871 | This is only approximate, | |
872 | it is used as part of the "hide unwriteable" option. Don't | |
873 | use it for anything security sensitive. | |
874 | ********************************************************************/ | |
875 | ||
876 | static bool user_can_write_fsp(struct files_struct *fsp) | |
877 | { | |
878 | /* | |
879 | * Never hide files from the root user. | |
880 | * We use (uid_t)0 here not sec_initial_uid() | |
881 | * as make test uses a single user context. | |
882 | */ | |
883 | ||
884 | if (get_current_uid(fsp->conn) == (uid_t)0) { | |
885 | return true; | |
886 | } | |
887 | ||
888 | if (fsp->fsp_flags.is_directory) { | |
889 | return true; | |
890 | } | |
891 | ||
892 | return can_write_to_fsp(fsp); | |
893 | } | |
894 | ||
a834a73e GC |
895 | /******************************************************************* |
896 | Is a file a "special" type ? | |
897 | ********************************************************************/ | |
898 | ||
eb8c658f TP |
899 | static bool file_is_special(connection_struct *conn, |
900 | const struct smb_filename *smb_fname) | |
a834a73e GC |
901 | { |
902 | /* | |
33bd9b4b JA |
903 | * Never hide files from the root user. |
904 | * We use (uid_t)0 here not sec_initial_uid() | |
905 | * as make test uses a single user context. | |
a834a73e GC |
906 | */ |
907 | ||
33bd9b4b | 908 | if (get_current_uid(conn) == (uid_t)0) { |
033fc98a | 909 | return False; |
33bd9b4b | 910 | } |
a834a73e | 911 | |
eb8c658f | 912 | SMB_ASSERT(VALID_STAT(smb_fname->st)); |
a834a73e | 913 | |
eb8c658f TP |
914 | if (S_ISREG(smb_fname->st.st_ex_mode) || |
915 | S_ISDIR(smb_fname->st.st_ex_mode) || | |
916 | S_ISLNK(smb_fname->st.st_ex_mode)) | |
a834a73e GC |
917 | return False; |
918 | ||
919 | return True; | |
920 | } | |
921 | ||
b25109d4 JA |
922 | /******************************************************************* |
923 | Should the file be seen by the client? | |
924 | ********************************************************************/ | |
925 | ||
9f2f4aff | 926 | bool is_visible_fsp(struct files_struct *fsp) |
b25109d4 JA |
927 | { |
928 | bool hide_unreadable = false; | |
929 | bool hide_unwriteable = false; | |
930 | bool hide_special = false; | |
931 | int hide_new_files_timeout = 0; | |
932 | const char *last_component = NULL; | |
933 | ||
934 | /* | |
935 | * If the file does not exist, there's no point checking | |
936 | * the configuration options. We succeed, on the basis that the | |
937 | * checks *might* have passed if the file was present. | |
938 | */ | |
939 | if (fsp == NULL) { | |
940 | return true; | |
941 | } | |
942 | ||
943 | hide_unreadable = lp_hide_unreadable(SNUM(fsp->conn)); | |
944 | hide_unwriteable = lp_hide_unwriteable_files(SNUM(fsp->conn)); | |
945 | hide_special = lp_hide_special_files(SNUM(fsp->conn)); | |
946 | hide_new_files_timeout = lp_hide_new_files_timeout(SNUM(fsp->conn)); | |
947 | ||
b919798f RB |
948 | if (!hide_unreadable && |
949 | !hide_unwriteable && | |
950 | !hide_special && | |
951 | (hide_new_files_timeout == 0)) | |
952 | { | |
953 | return true; | |
954 | } | |
955 | ||
ac58b0b9 | 956 | fsp = metadata_fsp(fsp); |
b25109d4 JA |
957 | |
958 | /* Get the last component of the base name. */ | |
959 | last_component = strrchr_m(fsp->fsp_name->base_name, '/'); | |
960 | if (!last_component) { | |
961 | last_component = fsp->fsp_name->base_name; | |
962 | } else { | |
963 | last_component++; /* Go past '/' */ | |
964 | } | |
965 | ||
966 | if (ISDOT(last_component) || ISDOTDOT(last_component)) { | |
967 | return true; /* . and .. are always visible. */ | |
968 | } | |
969 | ||
2dfd3038 JA |
970 | if (fsp_get_pathref_fd(fsp) == -1) { |
971 | /* | |
972 | * Symlink in POSIX mode or MS-DFS. | |
973 | * We've checked veto files so the | |
974 | * only thing we can check is the | |
975 | * hide_new_files_timeout. | |
976 | */ | |
be1431a8 VL |
977 | if ((hide_new_files_timeout != 0) && |
978 | !S_ISDIR(fsp->fsp_name->st.st_ex_mode)) { | |
2dfd3038 JA |
979 | double age = timespec_elapsed( |
980 | &fsp->fsp_name->st.st_ex_mtime); | |
981 | ||
982 | if (age < (double)hide_new_files_timeout) { | |
983 | return false; | |
984 | } | |
985 | } | |
986 | return true; | |
987 | } | |
988 | ||
f30f5dd2 VL |
989 | /* Honour _hide unreadable_ option */ |
990 | if (hide_unreadable && !user_can_read_fsp(fsp)) { | |
991 | DBG_DEBUG("file %s is unreadable.\n", fsp_str_dbg(fsp)); | |
992 | return false; | |
993 | } | |
b25109d4 | 994 | |
f30f5dd2 VL |
995 | /* Honour _hide unwriteable_ option */ |
996 | if (hide_unwriteable && !user_can_write_fsp(fsp)) { | |
997 | DBG_DEBUG("file %s is unwritable.\n", fsp_str_dbg(fsp)); | |
998 | return false; | |
999 | } | |
b25109d4 | 1000 | |
f30f5dd2 VL |
1001 | /* Honour _hide_special_ option */ |
1002 | if (hide_special && file_is_special(fsp->conn, fsp->fsp_name)) { | |
1003 | DBG_DEBUG("file %s is special.\n", fsp_str_dbg(fsp)); | |
1004 | return false; | |
1005 | } | |
1006 | ||
1007 | if ((hide_new_files_timeout != 0) && | |
1008 | !S_ISDIR(fsp->fsp_name->st.st_ex_mode)) { | |
1009 | double age = timespec_elapsed(&fsp->fsp_name->st.st_ex_mtime); | |
1010 | ||
1011 | if (age < (double)hide_new_files_timeout) { | |
1012 | return false; | |
b25109d4 JA |
1013 | } |
1014 | } | |
1015 | ||
1016 | return true; | |
1017 | } | |
1018 | ||
79eae9e3 | 1019 | static int smb_Dir_destructor(struct smb_Dir *dir_hnd) |
ec412b60 | 1020 | { |
24751598 JA |
1021 | files_struct *fsp = dir_hnd->fsp; |
1022 | ||
1023 | SMB_VFS_CLOSEDIR(dir_hnd->conn, dir_hnd->dir); | |
50ce9809 | 1024 | fsp_set_fd(fsp, -1); |
b6bcc4bd RB |
1025 | if (fsp->dptr != NULL) { |
1026 | SMB_ASSERT(fsp->dptr->dir_hnd == dir_hnd); | |
1027 | fsp->dptr->dir_hnd = NULL; | |
1028 | } | |
24751598 | 1029 | dir_hnd->fsp = NULL; |
ec412b60 VL |
1030 | return 0; |
1031 | } | |
1032 | ||
0e8fd339 | 1033 | /******************************************************************* |
3db52feb | 1034 | Open a directory. |
0e8fd339 | 1035 | ********************************************************************/ |
3db52feb | 1036 | |
54e0f250 | 1037 | static int smb_Dir_OpenDir_destructor(struct smb_Dir *dir_hnd) |
0e8fd339 | 1038 | { |
54e0f250 | 1039 | files_struct *fsp = dir_hnd->fsp; |
0122a4f4 | 1040 | |
54e0f250 RB |
1041 | smb_Dir_destructor(dir_hnd); |
1042 | file_free(NULL, fsp); | |
1043 | return 0; | |
0122a4f4 JA |
1044 | } |
1045 | ||
0c113e65 JA |
1046 | NTSTATUS OpenDir(TALLOC_CTX *mem_ctx, |
1047 | connection_struct *conn, | |
1048 | const struct smb_filename *smb_dname, | |
1049 | const char *mask, | |
1050 | uint32_t attr, | |
1051 | struct smb_Dir **_dir_hnd) | |
05a9898d | 1052 | { |
54e0f250 | 1053 | struct files_struct *fsp = NULL; |
05a9898d | 1054 | struct smb_Dir *dir_hnd = NULL; |
05a9898d JA |
1055 | NTSTATUS status; |
1056 | ||
84cf205f RB |
1057 | status = open_internal_dirfsp(conn, |
1058 | smb_dname, | |
1059 | O_RDONLY, | |
1060 | &fsp); | |
05a9898d | 1061 | if (!NT_STATUS_IS_OK(status)) { |
be201475 | 1062 | return status; |
05a9898d JA |
1063 | } |
1064 | ||
afd037df VL |
1065 | status = OpenDir_fsp(mem_ctx, conn, fsp, mask, attr, &dir_hnd); |
1066 | if (!NT_STATUS_IS_OK(status)) { | |
be201475 | 1067 | return status; |
05a9898d JA |
1068 | } |
1069 | ||
1070 | /* | |
3505285c VL |
1071 | * This overwrites the destructor set by OpenDir_fsp() but |
1072 | * smb_Dir_OpenDir_destructor() calls the OpenDir_fsp() | |
1073 | * destructor. | |
05a9898d | 1074 | */ |
eb5fa8ac | 1075 | talloc_set_destructor(dir_hnd, smb_Dir_OpenDir_destructor); |
be201475 VL |
1076 | |
1077 | *_dir_hnd = dir_hnd; | |
1078 | return NT_STATUS_OK; | |
34615180 JA |
1079 | } |
1080 | ||
973212e8 VL |
1081 | NTSTATUS OpenDir_from_pathref(TALLOC_CTX *mem_ctx, |
1082 | struct files_struct *dirfsp, | |
1083 | const char *mask, | |
1084 | uint32_t attr, | |
1085 | struct smb_Dir **_dir_hnd) | |
1086 | { | |
1087 | struct files_struct *fsp = NULL; | |
1088 | struct smb_Dir *dir_hnd = NULL; | |
1089 | NTSTATUS status; | |
1090 | ||
1091 | status = openat_internal_dir_from_pathref(dirfsp, O_RDONLY, &fsp); | |
1092 | if (!NT_STATUS_IS_OK(status)) { | |
1093 | return status; | |
1094 | } | |
1095 | ||
1096 | status = OpenDir_fsp(mem_ctx, fsp->conn, fsp, mask, attr, &dir_hnd); | |
1097 | if (!NT_STATUS_IS_OK(status)) { | |
1098 | return status; | |
1099 | } | |
1100 | ||
1101 | /* | |
1102 | * This overwrites the destructor set by OpenDir_fsp() but | |
1103 | * smb_Dir_OpenDir_destructor() calls the OpenDir_fsp() | |
1104 | * destructor. | |
1105 | */ | |
1106 | talloc_set_destructor(dir_hnd, smb_Dir_OpenDir_destructor); | |
1107 | ||
1108 | *_dir_hnd = dir_hnd; | |
1109 | return NT_STATUS_OK; | |
1110 | } | |
1111 | ||
0122a4f4 JA |
1112 | /******************************************************************* |
1113 | Open a directory from an fsp. | |
1114 | ********************************************************************/ | |
1115 | ||
afd037df VL |
1116 | static NTSTATUS OpenDir_fsp( |
1117 | TALLOC_CTX *mem_ctx, | |
1118 | connection_struct *conn, | |
1119 | files_struct *fsp, | |
1120 | const char *mask, | |
1121 | uint32_t attr, | |
1122 | struct smb_Dir **_dir_hnd) | |
0122a4f4 | 1123 | { |
cc4ac86b | 1124 | struct smb_Dir *dir_hnd = talloc_zero(mem_ctx, struct smb_Dir); |
afd037df | 1125 | NTSTATUS status; |
0122a4f4 | 1126 | |
cc4ac86b | 1127 | if (!dir_hnd) { |
afd037df | 1128 | return NT_STATUS_NO_MEMORY; |
86f15237 JA |
1129 | } |
1130 | ||
cb996cd5 | 1131 | if (!fsp->fsp_flags.is_directory) { |
afd037df | 1132 | status = NT_STATUS_INVALID_HANDLE; |
86f15237 JA |
1133 | goto fail; |
1134 | } | |
1135 | ||
50ce9809 | 1136 | if (fsp_get_io_fd(fsp) == -1) { |
afd037df | 1137 | status = NT_STATUS_INVALID_HANDLE; |
86f15237 | 1138 | goto fail; |
0122a4f4 JA |
1139 | } |
1140 | ||
cc4ac86b | 1141 | dir_hnd->conn = conn; |
66052fdc | 1142 | |
cc4ac86b JA |
1143 | dir_hnd->dir_smb_fname = cp_smb_filename(dir_hnd, fsp->fsp_name); |
1144 | if (!dir_hnd->dir_smb_fname) { | |
afd037df | 1145 | status = NT_STATUS_NO_MEMORY; |
de728fa8 JA |
1146 | goto fail; |
1147 | } | |
ec412b60 | 1148 | |
cc4ac86b | 1149 | dir_hnd->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr); |
cc4ac86b | 1150 | if (dir_hnd->dir == NULL) { |
afd037df | 1151 | status = map_nt_error_from_unix(errno); |
6dccfd63 | 1152 | goto fail; |
1607ebfe | 1153 | } |
6dccfd63 | 1154 | dir_hnd->fsp = fsp; |
ab1e97f8 JA |
1155 | if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) { |
1156 | dir_hnd->case_sensitive = true; | |
1157 | } else { | |
1158 | dir_hnd->case_sensitive = conn->case_sensitive; | |
1159 | } | |
1607ebfe | 1160 | |
cc4ac86b | 1161 | talloc_set_destructor(dir_hnd, smb_Dir_destructor); |
f6dfdf7d | 1162 | |
afd037df VL |
1163 | *_dir_hnd = dir_hnd; |
1164 | return NT_STATUS_OK; | |
127e77e6 | 1165 | |
de728fa8 | 1166 | fail: |
cc4ac86b | 1167 | TALLOC_FREE(dir_hnd); |
afd037df | 1168 | return status; |
0e8fd339 SRA |
1169 | } |
1170 | ||
0122a4f4 | 1171 | |
0e8fd339 | 1172 | /******************************************************************* |
25d345eb SD |
1173 | Read from a directory. |
1174 | Return directory entry, current offset, and optional stat information. | |
021011f9 | 1175 | Don't check for veto or invisible files. |
0e8fd339 | 1176 | ********************************************************************/ |
3db52feb | 1177 | |
daf6f2f7 | 1178 | const char *ReadDirName(struct smb_Dir *dir_hnd, char **ptalloced) |
0e8fd339 | 1179 | { |
f6650f5d VL |
1180 | const char *n; |
1181 | char *talloced = NULL; | |
ec072f1a | 1182 | connection_struct *conn = dir_hnd->conn; |
0e8fd339 | 1183 | |
e2fbe361 | 1184 | if (dir_hnd->file_number < 2) { |
ec072f1a | 1185 | if (dir_hnd->file_number == 0) { |
f6650f5d | 1186 | n = "."; |
c3fedee2 | 1187 | } else { |
f6650f5d | 1188 | n = ".."; |
c3fedee2 | 1189 | } |
ec072f1a | 1190 | dir_hnd->file_number++; |
f6650f5d | 1191 | *ptalloced = NULL; |
c3fedee2 | 1192 | return n; |
f2f9acc1 VL |
1193 | } |
1194 | ||
daf6f2f7 VL |
1195 | while ((n = vfs_readdirname(conn, |
1196 | dir_hnd->fsp, | |
1197 | dir_hnd->dir, | |
daf6f2f7 | 1198 | &talloced))) { |
c3fedee2 | 1199 | /* Ignore . and .. - we've already returned them. */ |
a0c897ba VL |
1200 | if (ISDOT(n) || ISDOTDOT(n)) { |
1201 | TALLOC_FREE(talloced); | |
1202 | continue; | |
c3fedee2 | 1203 | } |
f6650f5d | 1204 | *ptalloced = talloced; |
ec072f1a | 1205 | dir_hnd->file_number++; |
876f2deb | 1206 | return n; |
20f1cf6c | 1207 | } |
f6650f5d | 1208 | *ptalloced = NULL; |
de728fa8 JA |
1209 | return NULL; |
1210 | } | |
1211 | ||
741b0a97 JA |
1212 | /******************************************************************* |
1213 | Rewind to the start. | |
1214 | ********************************************************************/ | |
1215 | ||
e2fbe361 | 1216 | void RewindDir(struct smb_Dir *dir_hnd) |
741b0a97 | 1217 | { |
a5b876f3 JA |
1218 | SMB_VFS_REWINDDIR(dir_hnd->conn, dir_hnd->dir); |
1219 | dir_hnd->file_number = 0; | |
de728fa8 | 1220 | } |
0e8fd339 | 1221 | |
035fd720 VL |
1222 | struct files_below_forall_state { |
1223 | char *dirpath; | |
7d04b5a7 | 1224 | ssize_t dirpath_len; |
035fd720 VL |
1225 | int (*fn)(struct file_id fid, const struct share_mode_data *data, |
1226 | void *private_data); | |
1227 | void *private_data; | |
1228 | }; | |
1229 | ||
1230 | static int files_below_forall_fn(struct file_id fid, | |
1231 | const struct share_mode_data *data, | |
1232 | void *private_data) | |
1233 | { | |
1234 | struct files_below_forall_state *state = private_data; | |
1235 | char tmpbuf[PATH_MAX]; | |
1236 | char *fullpath, *to_free; | |
7d04b5a7 | 1237 | ssize_t len; |
035fd720 VL |
1238 | |
1239 | len = full_path_tos(data->servicepath, data->base_name, | |
1240 | tmpbuf, sizeof(tmpbuf), | |
1241 | &fullpath, &to_free); | |
1242 | if (len == -1) { | |
1243 | return 0; | |
1244 | } | |
1245 | if (state->dirpath_len >= len) { | |
1246 | /* | |
1247 | * Filter files above dirpath | |
1248 | */ | |
62c3a7a6 | 1249 | goto out; |
035fd720 VL |
1250 | } |
1251 | if (fullpath[state->dirpath_len] != '/') { | |
1252 | /* | |
1253 | * Filter file that don't have a path separator at the end of | |
1254 | * dirpath's length | |
1255 | */ | |
62c3a7a6 | 1256 | goto out; |
035fd720 VL |
1257 | } |
1258 | ||
15820061 | 1259 | if (memcmp(state->dirpath, fullpath, state->dirpath_len) != 0) { |
035fd720 VL |
1260 | /* |
1261 | * Not a parent | |
1262 | */ | |
62c3a7a6 | 1263 | goto out; |
035fd720 VL |
1264 | } |
1265 | ||
62c3a7a6 | 1266 | TALLOC_FREE(to_free); |
15820061 | 1267 | return state->fn(fid, data, state->private_data); |
62c3a7a6 SS |
1268 | |
1269 | out: | |
1270 | TALLOC_FREE(to_free); | |
1271 | return 0; | |
035fd720 VL |
1272 | } |
1273 | ||
1274 | static int files_below_forall(connection_struct *conn, | |
1275 | const struct smb_filename *dir_name, | |
1276 | int (*fn)(struct file_id fid, | |
1277 | const struct share_mode_data *data, | |
1278 | void *private_data), | |
1279 | void *private_data) | |
1280 | { | |
15820061 JA |
1281 | struct files_below_forall_state state = { |
1282 | .fn = fn, | |
1283 | .private_data = private_data, | |
1284 | }; | |
035fd720 VL |
1285 | int ret; |
1286 | char tmpbuf[PATH_MAX]; | |
1287 | char *to_free; | |
1288 | ||
1289 | state.dirpath_len = full_path_tos(conn->connectpath, | |
1290 | dir_name->base_name, | |
1291 | tmpbuf, sizeof(tmpbuf), | |
1292 | &state.dirpath, &to_free); | |
1293 | if (state.dirpath_len == -1) { | |
1294 | return -1; | |
035fd720 VL |
1295 | } |
1296 | ||
1297 | ret = share_mode_forall(files_below_forall_fn, &state); | |
1298 | TALLOC_FREE(to_free); | |
1299 | return ret; | |
1300 | } | |
1301 | ||
1302 | struct have_file_open_below_state { | |
1303 | bool found_one; | |
1304 | }; | |
1305 | ||
1306 | static int have_file_open_below_fn(struct file_id fid, | |
1307 | const struct share_mode_data *data, | |
1308 | void *private_data) | |
1309 | { | |
1310 | struct have_file_open_below_state *state = private_data; | |
1311 | state->found_one = true; | |
1312 | return 1; | |
1313 | } | |
1314 | ||
16f20287 | 1315 | bool have_file_open_below(connection_struct *conn, |
035fd720 VL |
1316 | const struct smb_filename *name) |
1317 | { | |
15820061 JA |
1318 | struct have_file_open_below_state state = { |
1319 | .found_one = false, | |
1320 | }; | |
035fd720 VL |
1321 | int ret; |
1322 | ||
1323 | if (!VALID_STAT(name->st)) { | |
1324 | return false; | |
1325 | } | |
1326 | if (!S_ISDIR(name->st.st_ex_mode)) { | |
1327 | return false; | |
1328 | } | |
1329 | ||
1330 | ret = files_below_forall(conn, name, have_file_open_below_fn, &state); | |
1331 | if (ret == -1) { | |
1332 | return false; | |
1333 | } | |
1334 | ||
1335 | return state.found_one; | |
1336 | } | |
1337 | ||
86e5659a JA |
1338 | /***************************************************************** |
1339 | Is this directory empty ? | |
1340 | *****************************************************************/ | |
1341 | ||
c5ad5029 | 1342 | NTSTATUS can_delete_directory_fsp(files_struct *fsp) |
86e5659a JA |
1343 | { |
1344 | NTSTATUS status = NT_STATUS_OK; | |
f6650f5d VL |
1345 | const char *dname = NULL; |
1346 | char *talloced = NULL; | |
c5ad5029 | 1347 | struct connection_struct *conn = fsp->conn; |
9027cc35 | 1348 | struct smb_Dir *dir_hnd = NULL; |
86e5659a | 1349 | |
0c113e65 | 1350 | status = OpenDir( |
9027cc35 VL |
1351 | talloc_tos(), conn, fsp->fsp_name, NULL, 0, &dir_hnd); |
1352 | if (!NT_STATUS_IS_OK(status)) { | |
1353 | return status; | |
86e5659a JA |
1354 | } |
1355 | ||
daf6f2f7 | 1356 | while ((dname = ReadDirName(dir_hnd, &talloced))) { |
f8a88473 JA |
1357 | struct smb_filename *smb_dname_full = NULL; |
1358 | struct smb_filename *direntry_fname = NULL; | |
1359 | char *fullname = NULL; | |
1360 | int ret; | |
1361 | ||
be381fd4 VL |
1362 | if (ISDOT(dname) || (ISDOTDOT(dname))) { |
1363 | TALLOC_FREE(talloced); | |
1364 | continue; | |
86e5659a | 1365 | } |
5d761421 JA |
1366 | if (IS_VETO_PATH(conn, dname)) { |
1367 | TALLOC_FREE(talloced); | |
1368 | continue; | |
1369 | } | |
f8a88473 JA |
1370 | |
1371 | fullname = talloc_asprintf(talloc_tos(), | |
1372 | "%s/%s", | |
1373 | fsp->fsp_name->base_name, | |
1374 | dname); | |
1375 | if (fullname == NULL) { | |
1376 | status = NT_STATUS_NO_MEMORY; | |
1377 | break; | |
1378 | } | |
1379 | ||
1380 | smb_dname_full = synthetic_smb_fname(talloc_tos(), | |
1381 | fullname, | |
1382 | NULL, | |
1383 | NULL, | |
1384 | fsp->fsp_name->twrp, | |
1385 | fsp->fsp_name->flags); | |
1386 | if (smb_dname_full == NULL) { | |
1387 | TALLOC_FREE(talloced); | |
1388 | TALLOC_FREE(fullname); | |
1389 | status = NT_STATUS_NO_MEMORY; | |
1390 | break; | |
1391 | } | |
1392 | ||
1393 | ret = SMB_VFS_LSTAT(conn, smb_dname_full); | |
1394 | if (ret != 0) { | |
1395 | status = map_nt_error_from_unix(errno); | |
1396 | TALLOC_FREE(talloced); | |
1397 | TALLOC_FREE(fullname); | |
1398 | TALLOC_FREE(smb_dname_full); | |
1399 | break; | |
1400 | } | |
1401 | ||
f8a88473 | 1402 | if (S_ISLNK(smb_dname_full->st.st_ex_mode)) { |
e9ef970e JA |
1403 | /* Could it be an msdfs link ? */ |
1404 | if (lp_host_msdfs() && | |
1405 | lp_msdfs_root(SNUM(conn))) { | |
1406 | struct smb_filename *smb_dname; | |
1407 | smb_dname = synthetic_smb_fname(talloc_tos(), | |
1408 | dname, | |
1409 | NULL, | |
1410 | &smb_dname_full->st, | |
1411 | fsp->fsp_name->twrp, | |
1412 | fsp->fsp_name->flags); | |
1413 | if (smb_dname == NULL) { | |
1414 | TALLOC_FREE(talloced); | |
1415 | TALLOC_FREE(fullname); | |
1416 | TALLOC_FREE(smb_dname_full); | |
1417 | status = NT_STATUS_NO_MEMORY; | |
1418 | break; | |
1419 | } | |
1420 | if (is_msdfs_link(fsp, smb_dname)) { | |
1421 | TALLOC_FREE(talloced); | |
1422 | TALLOC_FREE(fullname); | |
1423 | TALLOC_FREE(smb_dname_full); | |
1424 | TALLOC_FREE(smb_dname); | |
1425 | DBG_DEBUG("got msdfs link name %s " | |
1426 | "- can't delete directory %s\n", | |
1427 | dname, | |
1428 | fsp_str_dbg(fsp)); | |
1429 | status = NT_STATUS_DIRECTORY_NOT_EMPTY; | |
1430 | break; | |
1431 | } | |
1432 | TALLOC_FREE(smb_dname); | |
1433 | } | |
1434 | /* Not a DFS link - could it be a dangling symlink ? */ | |
1435 | ret = SMB_VFS_STAT(conn, smb_dname_full); | |
1436 | if (ret == -1 && (errno == ENOENT || errno == ELOOP)) { | |
1437 | /* | |
1438 | * Dangling symlink. | |
1439 | * Allow if "delete veto files = yes" | |
1440 | */ | |
1441 | if (lp_delete_veto_files(SNUM(conn))) { | |
1442 | TALLOC_FREE(talloced); | |
1443 | TALLOC_FREE(fullname); | |
1444 | TALLOC_FREE(smb_dname_full); | |
1445 | continue; | |
1446 | } | |
1447 | } | |
1448 | DBG_DEBUG("got symlink name %s - " | |
1449 | "can't delete directory %s\n", | |
1450 | dname, | |
1451 | fsp_str_dbg(fsp)); | |
f8a88473 JA |
1452 | TALLOC_FREE(talloced); |
1453 | TALLOC_FREE(fullname); | |
1454 | TALLOC_FREE(smb_dname_full); | |
f8a88473 JA |
1455 | status = NT_STATUS_DIRECTORY_NOT_EMPTY; |
1456 | break; | |
1457 | } | |
1458 | ||
1459 | /* Not a symlink, get a pathref. */ | |
1460 | status = synthetic_pathref(talloc_tos(), | |
1461 | fsp, | |
1462 | dname, | |
1463 | NULL, | |
1464 | &smb_dname_full->st, | |
1465 | fsp->fsp_name->twrp, | |
1466 | fsp->fsp_name->flags, | |
1467 | &direntry_fname); | |
1468 | if (!NT_STATUS_IS_OK(status)) { | |
1469 | status = map_nt_error_from_unix(errno); | |
f6650f5d | 1470 | TALLOC_FREE(talloced); |
f8a88473 JA |
1471 | TALLOC_FREE(fullname); |
1472 | TALLOC_FREE(smb_dname_full); | |
1473 | break; | |
1474 | } | |
1475 | ||
9f2f4aff | 1476 | if (!is_visible_fsp(direntry_fname->fsp)) { |
80503b46 JA |
1477 | /* |
1478 | * Hidden file. | |
1479 | * Allow if "delete veto files = yes" | |
1480 | */ | |
1481 | if (lp_delete_veto_files(SNUM(conn))) { | |
1482 | TALLOC_FREE(talloced); | |
1483 | TALLOC_FREE(fullname); | |
1484 | TALLOC_FREE(smb_dname_full); | |
1485 | TALLOC_FREE(direntry_fname); | |
1486 | continue; | |
1487 | } | |
86e5659a JA |
1488 | } |
1489 | ||
f8a88473 JA |
1490 | TALLOC_FREE(talloced); |
1491 | TALLOC_FREE(fullname); | |
1492 | TALLOC_FREE(smb_dname_full); | |
1493 | TALLOC_FREE(direntry_fname); | |
1494 | ||
1495 | DBG_DEBUG("got name %s - can't delete\n", dname); | |
86e5659a JA |
1496 | status = NT_STATUS_DIRECTORY_NOT_EMPTY; |
1497 | break; | |
1498 | } | |
f6650f5d | 1499 | TALLOC_FREE(talloced); |
ec412b60 | 1500 | TALLOC_FREE(dir_hnd); |
86e5659a | 1501 | |
035fd720 VL |
1502 | if (!NT_STATUS_IS_OK(status)) { |
1503 | return status; | |
1504 | } | |
1505 | ||
cff17f0a | 1506 | if (!(fsp->posix_flags & FSP_POSIX_FLAGS_RENAME) && |
035fd720 VL |
1507 | lp_strict_rename(SNUM(conn)) && |
1508 | have_file_open_below(fsp->conn, fsp->fsp_name)) | |
1509 | { | |
1510 | return NT_STATUS_ACCESS_DENIED; | |
1511 | } | |
1512 | ||
1513 | return NT_STATUS_OK; | |
86e5659a | 1514 | } |