Line data Source code
1 : /*
2 : Unix SMB/Netbios implementation.
3 : SMB client library implementation
4 : Copyright (C) Andrew Tridgell 1998
5 : Copyright (C) Richard Sharpe 2000, 2002
6 : Copyright (C) John Terpstra 2000
7 : Copyright (C) Tom Jansen (Ninja ISD) 2002
8 : Copyright (C) Derrell Lipman 2003-2008
9 : Copyright (C) Jeremy Allison 2007, 2008
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "includes.h"
26 : #include "libsmb/libsmb.h"
27 : #include "libsmbclient.h"
28 : #include "libsmb_internal.h"
29 : #include "../libcli/smb/smbXcli_base.h"
30 : #include "lib/util/time.h"
31 :
32 : /*
33 : * Generate an inode number from file name for those things that need it
34 : */
35 :
36 66 : static ino_t generate_inode(const char *name)
37 : {
38 66 : if (name == NULL) {
39 0 : return (ino_t)-1;
40 : }
41 66 : return (ino_t)str_checksum(name);
42 : }
43 :
44 : /*
45 : * Routine to put basic stat info into a stat structure ... Used by stat and
46 : * fstat below.
47 : */
48 :
49 215 : void setup_stat(struct stat *st,
50 : const char *fname,
51 : off_t size,
52 : int attr,
53 : ino_t ino,
54 : dev_t dev,
55 : struct timespec access_time_ts,
56 : struct timespec change_time_ts,
57 : struct timespec write_time_ts)
58 : {
59 215 : st->st_mode = 0;
60 :
61 215 : if (attr & FILE_ATTRIBUTE_DIRECTORY) {
62 86 : st->st_mode = (S_IFDIR | 0555);
63 : } else {
64 129 : st->st_mode = (S_IFREG | 0444);
65 : }
66 :
67 215 : if (attr & FILE_ATTRIBUTE_ARCHIVE) {
68 51 : st->st_mode |= S_IXUSR;
69 : }
70 215 : if (attr & FILE_ATTRIBUTE_SYSTEM) {
71 0 : st->st_mode |= S_IXGRP;
72 : }
73 215 : if (attr & FILE_ATTRIBUTE_HIDDEN) {
74 0 : st->st_mode |= S_IXOTH;
75 : }
76 215 : if (!(attr & FILE_ATTRIBUTE_READONLY)) {
77 215 : st->st_mode |= S_IWUSR;
78 : }
79 :
80 215 : st->st_size = size;
81 : #ifdef HAVE_STAT_ST_BLKSIZE
82 215 : st->st_blksize = 512;
83 : #endif
84 : #ifdef HAVE_STAT_ST_BLOCKS
85 215 : st->st_blocks = (size+511)/512;
86 : #endif
87 : #ifdef HAVE_STRUCT_STAT_ST_RDEV
88 215 : st->st_rdev = 0;
89 : #endif
90 215 : st->st_uid = getuid();
91 215 : st->st_gid = getgid();
92 :
93 215 : if (attr & FILE_ATTRIBUTE_DIRECTORY) {
94 86 : st->st_nlink = 2;
95 : } else {
96 129 : st->st_nlink = 1;
97 : }
98 :
99 215 : if (ino != 0) {
100 149 : st->st_ino = ino;
101 : } else {
102 66 : st->st_ino = generate_inode(fname);
103 : }
104 :
105 215 : st->st_dev = dev;
106 :
107 215 : st->st_atime = access_time_ts.tv_sec;
108 215 : set_atimensec(st, access_time_ts.tv_nsec);
109 :
110 215 : st->st_ctime = change_time_ts.tv_sec;
111 215 : set_ctimensec(st, change_time_ts.tv_nsec);
112 :
113 215 : st->st_mtime = write_time_ts.tv_sec;
114 215 : set_mtimensec(st, write_time_ts.tv_nsec);
115 215 : }
116 :
117 0 : void setup_stat_from_stat_ex(const struct stat_ex *stex,
118 : const char *fname,
119 : struct stat *st)
120 : {
121 0 : st->st_atime = stex->st_ex_atime.tv_sec;
122 0 : set_atimensec(st, stex->st_ex_atime.tv_nsec);
123 :
124 0 : st->st_ctime = stex->st_ex_ctime.tv_sec;
125 0 : set_ctimensec(st, stex->st_ex_ctime.tv_nsec);
126 :
127 0 : st->st_mtime = stex->st_ex_mtime.tv_sec;
128 0 : set_mtimensec(st, stex->st_ex_mtime.tv_nsec);
129 :
130 0 : st->st_mode = stex->st_ex_mode;
131 0 : st->st_size = stex->st_ex_size;
132 : #ifdef HAVE_STAT_ST_BLKSIZE
133 0 : st->st_blksize = 512;
134 : #endif
135 : #ifdef HAVE_STAT_ST_BLOCKS
136 0 : st->st_blocks = (st->st_size + 511) / 512;
137 : #endif
138 : #ifdef HAVE_STRUCT_STAT_ST_RDEV
139 0 : st->st_rdev = 0;
140 : #endif
141 0 : st->st_uid = stex->st_ex_uid;
142 0 : st->st_gid = stex->st_ex_gid;
143 :
144 0 : st->st_nlink = stex->st_ex_nlink;
145 :
146 0 : if (stex->st_ex_ino == 0) {
147 0 : st->st_ino = 0;
148 0 : if (fname != NULL) {
149 0 : st->st_ino = generate_inode(fname);
150 : }
151 : } else {
152 0 : st->st_ino = stex->st_ex_ino;
153 : }
154 :
155 0 : st->st_dev = stex->st_ex_dev;
156 :
157 0 : }
158 :
159 : /*
160 : * Routine to stat a file given a name
161 : */
162 :
163 : int
164 12 : SMBC_stat_ctx(SMBCCTX *context,
165 : const char *fname,
166 : struct stat *st)
167 : {
168 12 : SMBCSRV *srv = NULL;
169 12 : char *server = NULL;
170 12 : char *share = NULL;
171 12 : char *user = NULL;
172 12 : char *password = NULL;
173 12 : char *workgroup = NULL;
174 12 : char *path = NULL;
175 12 : uint16_t port = 0;
176 0 : NTSTATUS status;
177 12 : TALLOC_CTX *frame = talloc_stackframe();
178 :
179 12 : if (!context || !context->internal->initialized) {
180 0 : errno = EINVAL; /* Best I can think of ... */
181 0 : TALLOC_FREE(frame);
182 0 : return -1;
183 : }
184 :
185 12 : if (!fname) {
186 0 : errno = EINVAL;
187 0 : TALLOC_FREE(frame);
188 0 : return -1;
189 : }
190 :
191 12 : DEBUG(4, ("smbc_stat(%s)\n", fname));
192 :
193 12 : if (SMBC_parse_path(frame,
194 : context,
195 : fname,
196 : &workgroup,
197 : &server,
198 : &port,
199 : &share,
200 : &path,
201 : &user,
202 : &password,
203 : NULL)) {
204 0 : errno = EINVAL;
205 0 : TALLOC_FREE(frame);
206 0 : return -1;
207 : }
208 :
209 12 : if (!user || user[0] == (char)0) {
210 0 : user = talloc_strdup(frame, smbc_getUser(context));
211 0 : if (!user) {
212 0 : errno = ENOMEM;
213 0 : TALLOC_FREE(frame);
214 0 : return -1;
215 : }
216 : }
217 :
218 12 : srv = SMBC_server(frame, context, True,
219 : server, port, share, &workgroup, &user, &password);
220 12 : if (!srv) {
221 0 : TALLOC_FREE(frame);
222 0 : return -1; /* errno set by SMBC_server */
223 : }
224 :
225 12 : status = SMBC_getatr(context, srv, path, st);
226 12 : if (!NT_STATUS_IS_OK(status)) {
227 4 : TALLOC_FREE(frame);
228 4 : errno = cli_status_to_errno(status);
229 4 : return -1;
230 : }
231 :
232 8 : TALLOC_FREE(frame);
233 8 : return 0;
234 : }
235 :
236 : /*
237 : * Routine to stat a file given an fd
238 : */
239 :
240 : int
241 68 : SMBC_fstat_ctx(SMBCCTX *context,
242 : SMBCFILE *file,
243 : struct stat *st)
244 : {
245 0 : struct timespec change_time_ts;
246 0 : struct timespec access_time_ts;
247 0 : struct timespec write_time_ts;
248 0 : off_t size;
249 0 : uint32_t attr;
250 68 : char *server = NULL;
251 68 : char *share = NULL;
252 68 : char *user = NULL;
253 68 : char *password = NULL;
254 68 : char *path = NULL;
255 68 : char *targetpath = NULL;
256 68 : struct cli_state *targetcli = NULL;
257 68 : SMB_INO_T ino = 0;
258 68 : uint16_t port = 0;
259 68 : struct cli_credentials *creds = NULL;
260 68 : TALLOC_CTX *frame = talloc_stackframe();
261 0 : NTSTATUS status;
262 :
263 68 : if (!context || !context->internal->initialized) {
264 0 : errno = EINVAL;
265 0 : TALLOC_FREE(frame);
266 0 : return -1;
267 : }
268 :
269 68 : if (!SMBC_dlist_contains(context->internal->files, file)) {
270 0 : errno = EBADF;
271 0 : TALLOC_FREE(frame);
272 0 : return -1;
273 : }
274 :
275 68 : if (!file->file) {
276 0 : TALLOC_FREE(frame);
277 0 : return smbc_getFunctionFstatdir(context)(context, file, st);
278 : }
279 :
280 : /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
281 68 : if (SMBC_parse_path(frame,
282 : context,
283 68 : file->fname,
284 : NULL,
285 : &server,
286 : &port,
287 : &share,
288 : &path,
289 : &user,
290 : &password,
291 : NULL)) {
292 0 : errno = EINVAL;
293 0 : TALLOC_FREE(frame);
294 0 : return -1;
295 : }
296 :
297 68 : creds = context->internal->creds;
298 :
299 : /*d_printf(">>>fstat: resolving %s\n", path);*/
300 68 : status = cli_resolve_path(frame, "",
301 : creds,
302 68 : file->srv->cli, path,
303 : &targetcli, &targetpath);
304 68 : if (!NT_STATUS_IS_OK(status)) {
305 0 : d_printf("Could not resolve %s\n", path);
306 0 : errno = ENOENT;
307 0 : TALLOC_FREE(frame);
308 0 : return -1;
309 : }
310 : /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
311 :
312 68 : if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
313 : targetcli, file->cli_fd, &attr, &size,
314 : NULL,
315 : &access_time_ts,
316 : &write_time_ts,
317 : &change_time_ts,
318 : &ino))) {
319 0 : errno = EINVAL;
320 0 : TALLOC_FREE(frame);
321 0 : return -1;
322 : }
323 :
324 68 : setup_stat(st,
325 : path,
326 : size,
327 : attr,
328 : ino,
329 68 : file->srv->dev,
330 : access_time_ts,
331 : change_time_ts,
332 : write_time_ts);
333 :
334 68 : TALLOC_FREE(frame);
335 68 : return 0;
336 : }
337 :
338 :
339 : /*
340 : * Routine to obtain file system information given a path
341 : */
342 : int
343 0 : SMBC_statvfs_ctx(SMBCCTX *context,
344 : char *path,
345 : struct statvfs *st)
346 : {
347 0 : int ret;
348 0 : bool bIsDir;
349 0 : struct stat statbuf;
350 0 : SMBCFILE * pFile;
351 0 : TALLOC_CTX *frame = talloc_stackframe();
352 :
353 : /* Determine if the provided path is a file or a folder */
354 0 : if (SMBC_stat_ctx(context, path, &statbuf) < 0) {
355 0 : TALLOC_FREE(frame);
356 0 : return -1;
357 : }
358 :
359 : /* Is it a file or a directory? */
360 0 : if (S_ISDIR(statbuf.st_mode)) {
361 : /* It's a directory. */
362 0 : if ((pFile = SMBC_opendir_ctx(context, path)) == NULL) {
363 0 : TALLOC_FREE(frame);
364 0 : return -1;
365 : }
366 0 : bIsDir = true;
367 0 : } else if (S_ISREG(statbuf.st_mode)) {
368 : /* It's a file. */
369 0 : if ((pFile = SMBC_open_ctx(context, path,
370 : O_RDONLY, 0)) == NULL) {
371 0 : TALLOC_FREE(frame);
372 0 : return -1;
373 : }
374 0 : bIsDir = false;
375 : } else {
376 : /* It's neither a file nor a directory. Not supported. */
377 0 : TALLOC_FREE(frame);
378 0 : errno = ENOSYS;
379 0 : return -1;
380 : }
381 :
382 : /* Now we have an open file handle, so just use SMBC_fstatvfs */
383 0 : ret = SMBC_fstatvfs_ctx(context, pFile, st);
384 :
385 : /* Close the file or directory */
386 0 : if (bIsDir) {
387 0 : SMBC_closedir_ctx(context, pFile);
388 : } else {
389 0 : SMBC_close_ctx(context, pFile);
390 : }
391 :
392 0 : TALLOC_FREE(frame);
393 0 : return ret;
394 : }
395 :
396 :
397 : /*
398 : * Routine to obtain file system information given an fd
399 : */
400 :
401 : int
402 0 : SMBC_fstatvfs_ctx(SMBCCTX *context,
403 : SMBCFILE *file,
404 : struct statvfs *st)
405 : {
406 0 : unsigned long flags = 0;
407 0 : uint32_t fs_attrs = 0;
408 0 : struct cli_state *cli = file->srv->cli;
409 0 : struct smbXcli_tcon *tcon;
410 0 : TALLOC_CTX *frame = talloc_stackframe();
411 :
412 0 : if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
413 0 : tcon = cli->smb2.tcon;
414 : } else {
415 0 : tcon = cli->smb1.tcon;
416 : }
417 :
418 : /* Initialize all fields (at least until we actually use them) */
419 0 : ZERO_STRUCTP(st);
420 :
421 : /*
422 : * The state of each flag is such that the same bits are unset as
423 : * would typically be unset on a local file system on a POSIX OS. Thus
424 : * the bit is on, for example, only for case-insensitive file systems
425 : * since most POSIX file systems are case sensitive and fstatvfs()
426 : * would typically return zero in these bits on such a local file
427 : * system.
428 : */
429 :
430 : /* See if the server has UNIX CIFS support */
431 0 : if (! SERVER_HAS_UNIX_CIFS(cli)) {
432 0 : uint64_t total_allocation_units;
433 0 : uint64_t caller_allocation_units;
434 0 : uint64_t actual_allocation_units;
435 0 : uint64_t sectors_per_allocation_unit;
436 0 : uint64_t bytes_per_sector;
437 0 : NTSTATUS status;
438 :
439 : /* Nope. If size data is available... */
440 0 : status = cli_get_fs_full_size_info(cli,
441 : &total_allocation_units,
442 : &caller_allocation_units,
443 : &actual_allocation_units,
444 : §ors_per_allocation_unit,
445 : &bytes_per_sector);
446 0 : if (NT_STATUS_IS_OK(status)) {
447 :
448 : /* ... then provide it */
449 0 : st->f_bsize =
450 : (unsigned long) bytes_per_sector;
451 : #ifdef HAVE_FRSIZE
452 0 : st->f_frsize =
453 : (unsigned long) sectors_per_allocation_unit;
454 : #endif
455 0 : st->f_blocks =
456 : (fsblkcnt_t) total_allocation_units;
457 0 : st->f_bfree =
458 : (fsblkcnt_t) actual_allocation_units;
459 0 : st->f_bavail =
460 : (fsblkcnt_t) caller_allocation_units;
461 : }
462 :
463 0 : flags |= SMBC_VFS_FEATURE_NO_UNIXCIFS;
464 : } else {
465 0 : uint32_t optimal_transfer_size;
466 0 : uint32_t block_size;
467 0 : uint64_t total_blocks;
468 0 : uint64_t blocks_available;
469 0 : uint64_t user_blocks_available;
470 0 : uint64_t total_file_nodes;
471 0 : uint64_t free_file_nodes;
472 0 : uint64_t fs_identifier;
473 0 : NTSTATUS status;
474 :
475 : /* Has UNIXCIFS. If POSIX filesystem info is available... */
476 0 : status = cli_get_posix_fs_info(cli,
477 : &optimal_transfer_size,
478 : &block_size,
479 : &total_blocks,
480 : &blocks_available,
481 : &user_blocks_available,
482 : &total_file_nodes,
483 : &free_file_nodes,
484 : &fs_identifier);
485 0 : if (NT_STATUS_IS_OK(status)) {
486 :
487 : /* ... then what's provided here takes precedence. */
488 0 : st->f_bsize =
489 0 : (unsigned long) block_size;
490 0 : st->f_blocks =
491 : (fsblkcnt_t) total_blocks;
492 0 : st->f_bfree =
493 : (fsblkcnt_t) blocks_available;
494 0 : st->f_bavail =
495 : (fsblkcnt_t) user_blocks_available;
496 0 : st->f_files =
497 : (fsfilcnt_t) total_file_nodes;
498 0 : st->f_ffree =
499 : (fsfilcnt_t) free_file_nodes;
500 : #ifdef HAVE_FSID_INT
501 0 : st->f_fsid =
502 : (unsigned long) fs_identifier;
503 : #endif
504 : }
505 : }
506 :
507 : /* See if the share is case sensitive */
508 0 : if (!NT_STATUS_IS_OK(cli_get_fs_attr_info(cli, &fs_attrs))) {
509 : /*
510 : * We can't determine the case sensitivity of
511 : * the share. We have no choice but to use the
512 : * user-specified case sensitivity setting.
513 : */
514 0 : if (! smbc_getOptionCaseSensitive(context)) {
515 0 : flags |= SMBC_VFS_FEATURE_CASE_INSENSITIVE;
516 : }
517 : } else {
518 0 : if (! (fs_attrs & FILE_CASE_SENSITIVE_SEARCH)) {
519 0 : flags |= SMBC_VFS_FEATURE_CASE_INSENSITIVE;
520 : }
521 : }
522 :
523 : /* See if DFS is supported */
524 0 : if (smbXcli_conn_dfs_supported(cli->conn) &&
525 0 : smbXcli_tcon_is_dfs_share(tcon))
526 : {
527 0 : flags |= SMBC_VFS_FEATURE_DFS;
528 : }
529 :
530 : #if defined(HAVE_STATVFS_F_FLAG)
531 0 : st->f_flag = flags;
532 : #elif defined(HAVE_STATVFS_F_FLAGS)
533 : st->f_flags = flags;
534 : #endif
535 :
536 0 : TALLOC_FREE(frame);
537 0 : return 0;
538 : }
|