2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - read
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "vfs_posix.h"
24 #include "librpc/gen_ndr/xattr.h"
28 determine what access bits are needed for a call
30 static uint32_t pvfs_fileinfo_access(union smb_fileinfo
*info
)
34 switch (info
->generic
.level
) {
35 case RAW_FILEINFO_EA_LIST
:
36 case RAW_FILEINFO_ALL_EAS
:
37 needed
= SEC_FILE_READ_EA
;
40 case RAW_FILEINFO_IS_NAME_VALID
:
44 case RAW_FILEINFO_ACCESS_INFORMATION
:
48 case RAW_FILEINFO_SEC_DESC
:
50 if (info
->query_secdesc
.in
.secinfo_flags
& (SECINFO_OWNER
|SECINFO_GROUP
)) {
51 needed
|= SEC_STD_READ_CONTROL
;
53 if (info
->query_secdesc
.in
.secinfo_flags
& SECINFO_DACL
) {
54 needed
|= SEC_STD_READ_CONTROL
;
56 if (info
->query_secdesc
.in
.secinfo_flags
& SECINFO_SACL
) {
57 needed
|= SEC_FLAG_SYSTEM_SECURITY
;
62 needed
= SEC_FILE_READ_ATTRIBUTE
;
70 reply to a RAW_FILEINFO_EA_LIST call
72 NTSTATUS
pvfs_query_ea_list(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
73 struct pvfs_filename
*name
, int fd
,
75 struct ea_name
*names
,
76 struct smb_ea_list
*eas
)
80 struct xattr_DosEAs
*ealist
= talloc(mem_ctx
, struct xattr_DosEAs
);
83 status
= pvfs_doseas_load(pvfs
, name
, fd
, ealist
);
84 if (!NT_STATUS_IS_OK(status
)) {
87 eas
->eas
= talloc_array(mem_ctx
, struct ea_struct
, num_names
);
88 if (eas
->eas
== NULL
) {
89 return NT_STATUS_NO_MEMORY
;
91 eas
->num_eas
= num_names
;
92 for (i
=0;i
<num_names
;i
++) {
94 eas
->eas
[i
].flags
= 0;
95 eas
->eas
[i
].name
.s
= names
[i
].name
.s
;
96 eas
->eas
[i
].value
= data_blob(NULL
, 0);
97 for (j
=0;j
<ealist
->num_eas
;j
++) {
98 if (strcasecmp_m(eas
->eas
[i
].name
.s
,
99 ealist
->eas
[j
].name
) == 0) {
100 eas
->eas
[i
].value
= ealist
->eas
[j
].value
;
109 reply to a RAW_FILEINFO_ALL_EAS call
111 static NTSTATUS
pvfs_query_all_eas(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
112 struct pvfs_filename
*name
, int fd
,
113 struct smb_ea_list
*eas
)
117 struct xattr_DosEAs
*ealist
= talloc(mem_ctx
, struct xattr_DosEAs
);
120 status
= pvfs_doseas_load(pvfs
, name
, fd
, ealist
);
121 if (!NT_STATUS_IS_OK(status
)) {
124 eas
->eas
= talloc_array(mem_ctx
, struct ea_struct
, ealist
->num_eas
);
125 if (eas
->eas
== NULL
) {
126 return NT_STATUS_NO_MEMORY
;
129 for (i
=0;i
<ealist
->num_eas
;i
++) {
130 eas
->eas
[eas
->num_eas
].flags
= 0;
131 eas
->eas
[eas
->num_eas
].name
.s
= ealist
->eas
[i
].name
;
132 eas
->eas
[eas
->num_eas
].value
= ealist
->eas
[i
].value
;
139 approximately map a struct pvfs_filename to a generic fileinfo struct
141 static NTSTATUS
pvfs_map_fileinfo(struct pvfs_state
*pvfs
,
142 struct ntvfs_request
*req
,
143 struct pvfs_filename
*name
, union smb_fileinfo
*info
,
146 switch (info
->generic
.level
) {
147 case RAW_FILEINFO_GENERIC
:
148 return NT_STATUS_INVALID_LEVEL
;
150 case RAW_FILEINFO_GETATTR
:
151 info
->getattr
.out
.attrib
= name
->dos
.attrib
;
152 info
->getattr
.out
.size
= name
->st
.st_size
;
153 info
->getattr
.out
.write_time
= nt_time_to_unix(name
->dos
.write_time
);
156 case RAW_FILEINFO_GETATTRE
:
157 case RAW_FILEINFO_STANDARD
:
158 info
->standard
.out
.create_time
= nt_time_to_unix(name
->dos
.create_time
);
159 info
->standard
.out
.access_time
= nt_time_to_unix(name
->dos
.access_time
);
160 info
->standard
.out
.write_time
= nt_time_to_unix(name
->dos
.write_time
);
161 info
->standard
.out
.size
= name
->st
.st_size
;
162 info
->standard
.out
.alloc_size
= name
->dos
.alloc_size
;
163 info
->standard
.out
.attrib
= name
->dos
.attrib
;
166 case RAW_FILEINFO_EA_SIZE
:
167 info
->ea_size
.out
.create_time
= nt_time_to_unix(name
->dos
.create_time
);
168 info
->ea_size
.out
.access_time
= nt_time_to_unix(name
->dos
.access_time
);
169 info
->ea_size
.out
.write_time
= nt_time_to_unix(name
->dos
.write_time
);
170 info
->ea_size
.out
.size
= name
->st
.st_size
;
171 info
->ea_size
.out
.alloc_size
= name
->dos
.alloc_size
;
172 info
->ea_size
.out
.attrib
= name
->dos
.attrib
;
173 info
->ea_size
.out
.ea_size
= name
->dos
.ea_size
;
176 case RAW_FILEINFO_EA_LIST
:
177 return pvfs_query_ea_list(pvfs
, req
, name
, fd
,
178 info
->ea_list
.in
.num_names
,
179 info
->ea_list
.in
.ea_names
,
182 case RAW_FILEINFO_ALL_EAS
:
183 return pvfs_query_all_eas(pvfs
, req
, name
, fd
, &info
->all_eas
.out
);
185 case RAW_FILEINFO_SMB2_ALL_EAS
: {
186 NTSTATUS status
= pvfs_query_all_eas(pvfs
, req
, name
, fd
, &info
->all_eas
.out
);
187 if (NT_STATUS_IS_OK(status
) &&
188 info
->all_eas
.out
.num_eas
== 0) {
189 return NT_STATUS_NO_EAS_ON_FILE
;
194 case RAW_FILEINFO_IS_NAME_VALID
:
197 case RAW_FILEINFO_BASIC_INFO
:
198 case RAW_FILEINFO_BASIC_INFORMATION
:
199 info
->basic_info
.out
.create_time
= name
->dos
.create_time
;
200 info
->basic_info
.out
.access_time
= name
->dos
.access_time
;
201 info
->basic_info
.out
.write_time
= name
->dos
.write_time
;
202 info
->basic_info
.out
.change_time
= name
->dos
.change_time
;
203 info
->basic_info
.out
.attrib
= name
->dos
.attrib
;
206 case RAW_FILEINFO_STANDARD_INFO
:
207 case RAW_FILEINFO_STANDARD_INFORMATION
:
208 info
->standard_info
.out
.alloc_size
= name
->dos
.alloc_size
;
209 info
->standard_info
.out
.size
= name
->st
.st_size
;
210 info
->standard_info
.out
.nlink
= name
->dos
.nlink
;
211 info
->standard_info
.out
.delete_pending
= 0; /* only for qfileinfo */
212 info
->standard_info
.out
.directory
=
213 (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)? 1 : 0;
216 case RAW_FILEINFO_EA_INFO
:
217 case RAW_FILEINFO_EA_INFORMATION
:
218 info
->ea_info
.out
.ea_size
= name
->dos
.ea_size
;
221 case RAW_FILEINFO_NAME_INFO
:
222 case RAW_FILEINFO_NAME_INFORMATION
:
223 if (req
->ctx
->protocol
== PROTOCOL_SMB2
) {
224 /* strange that SMB2 doesn't have this */
225 return NT_STATUS_NOT_SUPPORTED
;
227 info
->name_info
.out
.fname
.s
= name
->original_name
;
230 case RAW_FILEINFO_ALL_INFO
:
231 case RAW_FILEINFO_ALL_INFORMATION
:
232 info
->all_info
.out
.create_time
= name
->dos
.create_time
;
233 info
->all_info
.out
.access_time
= name
->dos
.access_time
;
234 info
->all_info
.out
.write_time
= name
->dos
.write_time
;
235 info
->all_info
.out
.change_time
= name
->dos
.change_time
;
236 info
->all_info
.out
.attrib
= name
->dos
.attrib
;
237 info
->all_info
.out
.alloc_size
= name
->dos
.alloc_size
;
238 info
->all_info
.out
.size
= name
->st
.st_size
;
239 info
->all_info
.out
.nlink
= name
->dos
.nlink
;
240 info
->all_info
.out
.delete_pending
= 0; /* only set by qfileinfo */
241 info
->all_info
.out
.directory
=
242 (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)? 1 : 0;
243 info
->all_info
.out
.ea_size
= name
->dos
.ea_size
;
244 info
->all_info
.out
.fname
.s
= name
->original_name
;
247 case RAW_FILEINFO_ALT_NAME_INFO
:
248 case RAW_FILEINFO_ALT_NAME_INFORMATION
:
249 info
->name_info
.out
.fname
.s
= pvfs_short_name(pvfs
, name
, name
);
252 case RAW_FILEINFO_STREAM_INFO
:
253 case RAW_FILEINFO_STREAM_INFORMATION
:
254 return pvfs_stream_information(pvfs
, req
, name
, fd
, &info
->stream_info
.out
);
256 case RAW_FILEINFO_COMPRESSION_INFO
:
257 case RAW_FILEINFO_COMPRESSION_INFORMATION
:
258 info
->compression_info
.out
.compressed_size
= name
->st
.st_size
;
259 info
->compression_info
.out
.format
= 0;
260 info
->compression_info
.out
.unit_shift
= 0;
261 info
->compression_info
.out
.chunk_shift
= 0;
262 info
->compression_info
.out
.cluster_shift
= 0;
265 case RAW_FILEINFO_INTERNAL_INFORMATION
:
266 info
->internal_information
.out
.file_id
= name
->dos
.file_id
;
269 case RAW_FILEINFO_ACCESS_INFORMATION
:
270 info
->access_information
.out
.access_flags
= 0; /* only set by qfileinfo */
273 case RAW_FILEINFO_POSITION_INFORMATION
:
274 info
->position_information
.out
.position
= 0; /* only set by qfileinfo */
277 case RAW_FILEINFO_MODE_INFORMATION
:
278 info
->mode_information
.out
.mode
= 0; /* only set by qfileinfo */
281 case RAW_FILEINFO_ALIGNMENT_INFORMATION
:
282 info
->alignment_information
.out
.alignment_requirement
= 0;
285 case RAW_FILEINFO_NETWORK_OPEN_INFORMATION
:
286 info
->network_open_information
.out
.create_time
= name
->dos
.create_time
;
287 info
->network_open_information
.out
.access_time
= name
->dos
.access_time
;
288 info
->network_open_information
.out
.write_time
= name
->dos
.write_time
;
289 info
->network_open_information
.out
.change_time
= name
->dos
.change_time
;
290 info
->network_open_information
.out
.alloc_size
= name
->dos
.alloc_size
;
291 info
->network_open_information
.out
.size
= name
->st
.st_size
;
292 info
->network_open_information
.out
.attrib
= name
->dos
.attrib
;
295 case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION
:
296 info
->attribute_tag_information
.out
.attrib
= name
->dos
.attrib
;
297 info
->attribute_tag_information
.out
.reparse_tag
= 0;
300 case RAW_FILEINFO_SEC_DESC
:
301 return pvfs_acl_query(pvfs
, req
, name
, fd
, info
);
303 case RAW_FILEINFO_SMB2_ALL_INFORMATION
:
304 info
->all_info2
.out
.create_time
= name
->dos
.create_time
;
305 info
->all_info2
.out
.access_time
= name
->dos
.access_time
;
306 info
->all_info2
.out
.write_time
= name
->dos
.write_time
;
307 info
->all_info2
.out
.change_time
= name
->dos
.change_time
;
308 info
->all_info2
.out
.attrib
= name
->dos
.attrib
;
309 info
->all_info2
.out
.unknown1
= 0;
310 info
->all_info2
.out
.alloc_size
= name
->dos
.alloc_size
;
311 info
->all_info2
.out
.size
= name
->st
.st_size
;
312 info
->all_info2
.out
.nlink
= name
->dos
.nlink
;
313 info
->all_info2
.out
.delete_pending
= 0; /* only set by qfileinfo */
314 info
->all_info2
.out
.directory
=
315 (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)? 1 : 0;
316 info
->all_info2
.out
.file_id
= name
->dos
.file_id
;
317 info
->all_info2
.out
.ea_size
= name
->dos
.ea_size
;
318 info
->all_info2
.out
.access_mask
= 0; /* only set by qfileinfo */
319 info
->all_info2
.out
.position
= 0; /* only set by qfileinfo */
320 info
->all_info2
.out
.mode
= 0; /* only set by qfileinfo */
321 /* windows wants the full path on disk for this
322 result, but I really don't want to expose that on
323 the wire, so I'll give the path with a share
324 prefix, which is a good approximation */
325 info
->all_info2
.out
.fname
.s
= talloc_asprintf(req
, "\\%s\\%s",
327 name
->original_name
);
328 NT_STATUS_HAVE_NO_MEMORY(info
->all_info2
.out
.fname
.s
);
332 return NT_STATUS_INVALID_LEVEL
;
336 return info on a pathname
338 NTSTATUS
pvfs_qpathinfo(struct ntvfs_module_context
*ntvfs
,
339 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
341 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
343 struct pvfs_filename
*name
;
346 /* resolve the cifs name to a posix name */
347 status
= pvfs_resolve_name(pvfs
, req
, info
->generic
.in
.file
.path
, PVFS_RESOLVE_STREAMS
, &name
);
348 if (!NT_STATUS_IS_OK(status
)) {
352 if (!name
->stream_exists
) {
353 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
356 status
= pvfs_can_stat(pvfs
, req
, name
);
357 if (!NT_STATUS_IS_OK(status
)) {
361 status
= pvfs_access_check_simple(pvfs
, req
, name
,
362 pvfs_fileinfo_access(info
));
363 if (!NT_STATUS_IS_OK(status
)) {
367 status
= pvfs_map_fileinfo(pvfs
, req
, name
, info
, -1);
373 query info on a open file
375 NTSTATUS
pvfs_qfileinfo(struct ntvfs_module_context
*ntvfs
,
376 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
378 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
381 struct pvfs_file_handle
*h
;
383 uint32_t access_needed
;
385 f
= pvfs_find_fd(pvfs
, req
, info
->generic
.in
.file
.ntvfs
);
387 return NT_STATUS_INVALID_HANDLE
;
391 access_needed
= pvfs_fileinfo_access(info
);
392 if ((f
->access_mask
& access_needed
) != access_needed
) {
393 return NT_STATUS_ACCESS_DENIED
;
396 /* update the file information */
397 status
= pvfs_resolve_name_handle(pvfs
, h
);
398 if (!NT_STATUS_IS_OK(status
)) {
402 status
= pvfs_map_fileinfo(pvfs
, req
, h
->name
, info
, h
->fd
);
404 /* a qfileinfo can fill in a bit more info than a qpathinfo -
405 now modify the levels that need to be fixed up */
406 switch (info
->generic
.level
) {
407 case RAW_FILEINFO_STANDARD_INFO
:
408 case RAW_FILEINFO_STANDARD_INFORMATION
:
409 if (pvfs_delete_on_close_set(pvfs
, h
)) {
410 info
->standard_info
.out
.delete_pending
= 1;
411 info
->standard_info
.out
.nlink
--;
415 case RAW_FILEINFO_ALL_INFO
:
416 case RAW_FILEINFO_ALL_INFORMATION
:
417 if (pvfs_delete_on_close_set(pvfs
, h
)) {
418 info
->all_info
.out
.delete_pending
= 1;
419 info
->all_info
.out
.nlink
--;
423 case RAW_FILEINFO_POSITION_INFORMATION
:
424 info
->position_information
.out
.position
= h
->position
;
427 case RAW_FILEINFO_ACCESS_INFORMATION
:
428 info
->access_information
.out
.access_flags
= f
->access_mask
;
431 case RAW_FILEINFO_MODE_INFORMATION
:
432 info
->mode_information
.out
.mode
= h
->mode
;
435 case RAW_FILEINFO_SMB2_ALL_INFORMATION
:
436 if (pvfs_delete_on_close_set(pvfs
, h
)) {
437 info
->all_info2
.out
.delete_pending
= 1;
438 info
->all_info2
.out
.nlink
--;
440 info
->all_info2
.out
.position
= h
->position
;
441 info
->all_info2
.out
.access_mask
= f
->access_mask
;
442 info
->all_info2
.out
.mode
= h
->mode
;