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_STREAM_INFO
:
49 case RAW_FILEINFO_STREAM_INFORMATION
:
53 case RAW_FILEINFO_SEC_DESC
:
55 if (info
->query_secdesc
.in
.secinfo_flags
& (SECINFO_OWNER
|SECINFO_GROUP
)) {
56 needed
|= SEC_STD_READ_CONTROL
;
58 if (info
->query_secdesc
.in
.secinfo_flags
& SECINFO_DACL
) {
59 needed
|= SEC_STD_READ_CONTROL
;
61 if (info
->query_secdesc
.in
.secinfo_flags
& SECINFO_SACL
) {
62 needed
|= SEC_FLAG_SYSTEM_SECURITY
;
67 needed
= SEC_FILE_READ_ATTRIBUTE
;
75 reply to a RAW_FILEINFO_EA_LIST call
77 NTSTATUS
pvfs_query_ea_list(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
78 struct pvfs_filename
*name
, int fd
,
79 unsigned int num_names
,
80 struct ea_name
*names
,
81 struct smb_ea_list
*eas
)
85 struct xattr_DosEAs
*ealist
= talloc(mem_ctx
, struct xattr_DosEAs
);
88 status
= pvfs_doseas_load(pvfs
, name
, fd
, ealist
);
89 if (!NT_STATUS_IS_OK(status
)) {
92 eas
->eas
= talloc_array(mem_ctx
, struct ea_struct
, num_names
);
93 if (eas
->eas
== NULL
) {
94 return NT_STATUS_NO_MEMORY
;
96 eas
->num_eas
= num_names
;
97 for (i
=0;i
<num_names
;i
++) {
99 eas
->eas
[i
].flags
= 0;
100 eas
->eas
[i
].name
.s
= names
[i
].name
.s
;
101 eas
->eas
[i
].value
= data_blob(NULL
, 0);
102 for (j
=0;j
<ealist
->num_eas
;j
++) {
103 if (strcasecmp_m(eas
->eas
[i
].name
.s
,
104 ealist
->eas
[j
].name
) == 0) {
105 eas
->eas
[i
].value
= ealist
->eas
[j
].value
;
114 reply to a RAW_FILEINFO_ALL_EAS call
116 static NTSTATUS
pvfs_query_all_eas(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
117 struct pvfs_filename
*name
, int fd
,
118 struct smb_ea_list
*eas
)
122 struct xattr_DosEAs
*ealist
= talloc(mem_ctx
, struct xattr_DosEAs
);
125 status
= pvfs_doseas_load(pvfs
, name
, fd
, ealist
);
126 if (!NT_STATUS_IS_OK(status
)) {
129 eas
->eas
= talloc_array(mem_ctx
, struct ea_struct
, ealist
->num_eas
);
130 if (eas
->eas
== NULL
) {
131 return NT_STATUS_NO_MEMORY
;
134 for (i
=0;i
<ealist
->num_eas
;i
++) {
135 eas
->eas
[eas
->num_eas
].flags
= 0;
136 eas
->eas
[eas
->num_eas
].name
.s
= ealist
->eas
[i
].name
;
137 eas
->eas
[eas
->num_eas
].value
= ealist
->eas
[i
].value
;
144 approximately map a struct pvfs_filename to a generic fileinfo struct
146 static NTSTATUS
pvfs_map_fileinfo(struct pvfs_state
*pvfs
,
147 struct ntvfs_request
*req
,
148 struct pvfs_filename
*name
, union smb_fileinfo
*info
,
151 switch (info
->generic
.level
) {
152 case RAW_FILEINFO_GENERIC
:
153 return NT_STATUS_INVALID_LEVEL
;
155 case RAW_FILEINFO_GETATTR
:
156 info
->getattr
.out
.attrib
= name
->dos
.attrib
;
157 info
->getattr
.out
.size
= name
->st
.st_size
;
158 info
->getattr
.out
.write_time
= nt_time_to_unix(name
->dos
.write_time
);
161 case RAW_FILEINFO_GETATTRE
:
162 case RAW_FILEINFO_STANDARD
:
163 info
->standard
.out
.create_time
= nt_time_to_unix(name
->dos
.create_time
);
164 info
->standard
.out
.access_time
= nt_time_to_unix(name
->dos
.access_time
);
165 info
->standard
.out
.write_time
= nt_time_to_unix(name
->dos
.write_time
);
166 info
->standard
.out
.size
= name
->st
.st_size
;
167 info
->standard
.out
.alloc_size
= name
->dos
.alloc_size
;
168 info
->standard
.out
.attrib
= name
->dos
.attrib
;
171 case RAW_FILEINFO_EA_SIZE
:
172 info
->ea_size
.out
.create_time
= nt_time_to_unix(name
->dos
.create_time
);
173 info
->ea_size
.out
.access_time
= nt_time_to_unix(name
->dos
.access_time
);
174 info
->ea_size
.out
.write_time
= nt_time_to_unix(name
->dos
.write_time
);
175 info
->ea_size
.out
.size
= name
->st
.st_size
;
176 info
->ea_size
.out
.alloc_size
= name
->dos
.alloc_size
;
177 info
->ea_size
.out
.attrib
= name
->dos
.attrib
;
178 info
->ea_size
.out
.ea_size
= name
->dos
.ea_size
;
181 case RAW_FILEINFO_EA_LIST
:
182 return pvfs_query_ea_list(pvfs
, req
, name
, fd
,
183 info
->ea_list
.in
.num_names
,
184 info
->ea_list
.in
.ea_names
,
187 case RAW_FILEINFO_ALL_EAS
:
188 return pvfs_query_all_eas(pvfs
, req
, name
, fd
, &info
->all_eas
.out
);
190 case RAW_FILEINFO_SMB2_ALL_EAS
: {
191 NTSTATUS status
= pvfs_query_all_eas(pvfs
, req
, name
, fd
, &info
->all_eas
.out
);
192 if (NT_STATUS_IS_OK(status
) &&
193 info
->all_eas
.out
.num_eas
== 0) {
194 return NT_STATUS_NO_EAS_ON_FILE
;
199 case RAW_FILEINFO_IS_NAME_VALID
:
202 case RAW_FILEINFO_BASIC_INFO
:
203 case RAW_FILEINFO_BASIC_INFORMATION
:
204 info
->basic_info
.out
.create_time
= name
->dos
.create_time
;
205 info
->basic_info
.out
.access_time
= name
->dos
.access_time
;
206 info
->basic_info
.out
.write_time
= name
->dos
.write_time
;
207 info
->basic_info
.out
.change_time
= name
->dos
.change_time
;
208 info
->basic_info
.out
.attrib
= name
->dos
.attrib
;
211 case RAW_FILEINFO_STANDARD_INFO
:
212 case RAW_FILEINFO_STANDARD_INFORMATION
:
213 info
->standard_info
.out
.alloc_size
= name
->dos
.alloc_size
;
214 info
->standard_info
.out
.size
= name
->st
.st_size
;
215 info
->standard_info
.out
.nlink
= name
->dos
.nlink
;
216 info
->standard_info
.out
.delete_pending
= 0; /* only for qfileinfo */
217 info
->standard_info
.out
.directory
=
218 (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)? 1 : 0;
221 case RAW_FILEINFO_EA_INFO
:
222 case RAW_FILEINFO_EA_INFORMATION
:
223 info
->ea_info
.out
.ea_size
= name
->dos
.ea_size
;
226 case RAW_FILEINFO_NAME_INFO
:
227 case RAW_FILEINFO_NAME_INFORMATION
:
228 if (req
->ctx
->protocol
== PROTOCOL_SMB2
) {
229 /* strange that SMB2 doesn't have this */
230 return NT_STATUS_NOT_SUPPORTED
;
232 info
->name_info
.out
.fname
.s
= name
->original_name
;
235 case RAW_FILEINFO_ALL_INFO
:
236 case RAW_FILEINFO_ALL_INFORMATION
:
237 info
->all_info
.out
.create_time
= name
->dos
.create_time
;
238 info
->all_info
.out
.access_time
= name
->dos
.access_time
;
239 info
->all_info
.out
.write_time
= name
->dos
.write_time
;
240 info
->all_info
.out
.change_time
= name
->dos
.change_time
;
241 info
->all_info
.out
.attrib
= name
->dos
.attrib
;
242 info
->all_info
.out
.alloc_size
= name
->dos
.alloc_size
;
243 info
->all_info
.out
.size
= name
->st
.st_size
;
244 info
->all_info
.out
.nlink
= name
->dos
.nlink
;
245 info
->all_info
.out
.delete_pending
= 0; /* only set by qfileinfo */
246 info
->all_info
.out
.directory
=
247 (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)? 1 : 0;
248 info
->all_info
.out
.ea_size
= name
->dos
.ea_size
;
249 info
->all_info
.out
.fname
.s
= name
->original_name
;
252 case RAW_FILEINFO_ALT_NAME_INFO
:
253 case RAW_FILEINFO_ALT_NAME_INFORMATION
:
254 info
->name_info
.out
.fname
.s
= pvfs_short_name(pvfs
, name
, name
);
257 case RAW_FILEINFO_STREAM_INFO
:
258 case RAW_FILEINFO_STREAM_INFORMATION
:
259 return pvfs_stream_information(pvfs
, req
, name
, fd
, &info
->stream_info
.out
);
261 case RAW_FILEINFO_COMPRESSION_INFO
:
262 case RAW_FILEINFO_COMPRESSION_INFORMATION
:
263 info
->compression_info
.out
.compressed_size
= name
->st
.st_size
;
264 info
->compression_info
.out
.format
= 0;
265 info
->compression_info
.out
.unit_shift
= 0;
266 info
->compression_info
.out
.chunk_shift
= 0;
267 info
->compression_info
.out
.cluster_shift
= 0;
270 case RAW_FILEINFO_INTERNAL_INFORMATION
:
271 info
->internal_information
.out
.file_id
= name
->dos
.file_id
;
274 case RAW_FILEINFO_ACCESS_INFORMATION
:
275 info
->access_information
.out
.access_flags
= 0; /* only set by qfileinfo */
278 case RAW_FILEINFO_POSITION_INFORMATION
:
279 info
->position_information
.out
.position
= 0; /* only set by qfileinfo */
282 case RAW_FILEINFO_MODE_INFORMATION
:
283 info
->mode_information
.out
.mode
= 0; /* only set by qfileinfo */
286 case RAW_FILEINFO_ALIGNMENT_INFORMATION
:
287 info
->alignment_information
.out
.alignment_requirement
= 0;
290 case RAW_FILEINFO_NETWORK_OPEN_INFORMATION
:
291 info
->network_open_information
.out
.create_time
= name
->dos
.create_time
;
292 info
->network_open_information
.out
.access_time
= name
->dos
.access_time
;
293 info
->network_open_information
.out
.write_time
= name
->dos
.write_time
;
294 info
->network_open_information
.out
.change_time
= name
->dos
.change_time
;
295 info
->network_open_information
.out
.alloc_size
= name
->dos
.alloc_size
;
296 info
->network_open_information
.out
.size
= name
->st
.st_size
;
297 info
->network_open_information
.out
.attrib
= name
->dos
.attrib
;
300 case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION
:
301 info
->attribute_tag_information
.out
.attrib
= name
->dos
.attrib
;
302 info
->attribute_tag_information
.out
.reparse_tag
= 0;
305 case RAW_FILEINFO_SEC_DESC
:
306 return pvfs_acl_query(pvfs
, req
, name
, fd
, info
);
308 case RAW_FILEINFO_SMB2_ALL_INFORMATION
:
309 info
->all_info2
.out
.create_time
= name
->dos
.create_time
;
310 info
->all_info2
.out
.access_time
= name
->dos
.access_time
;
311 info
->all_info2
.out
.write_time
= name
->dos
.write_time
;
312 info
->all_info2
.out
.change_time
= name
->dos
.change_time
;
313 info
->all_info2
.out
.attrib
= name
->dos
.attrib
;
314 info
->all_info2
.out
.unknown1
= 0;
315 info
->all_info2
.out
.alloc_size
= name
->dos
.alloc_size
;
316 info
->all_info2
.out
.size
= name
->st
.st_size
;
317 info
->all_info2
.out
.nlink
= name
->dos
.nlink
;
318 info
->all_info2
.out
.delete_pending
= 0; /* only set by qfileinfo */
319 info
->all_info2
.out
.directory
=
320 (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)? 1 : 0;
321 info
->all_info2
.out
.file_id
= name
->dos
.file_id
;
322 info
->all_info2
.out
.ea_size
= name
->dos
.ea_size
;
323 info
->all_info2
.out
.access_mask
= 0; /* only set by qfileinfo */
324 info
->all_info2
.out
.position
= 0; /* only set by qfileinfo */
325 info
->all_info2
.out
.mode
= 0; /* only set by qfileinfo */
326 info
->all_info2
.out
.alignment_requirement
= 0;
327 /* windows wants the full path on disk for this
328 result, but I really don't want to expose that on
329 the wire, so I'll give the path with a share
330 prefix, which is a good approximation */
331 info
->all_info2
.out
.fname
.s
= talloc_asprintf(req
, "\\%s\\%s",
333 name
->original_name
);
334 NT_STATUS_HAVE_NO_MEMORY(info
->all_info2
.out
.fname
.s
);
338 return NT_STATUS_INVALID_LEVEL
;
342 return info on a pathname
344 NTSTATUS
pvfs_qpathinfo(struct ntvfs_module_context
*ntvfs
,
345 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
347 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
349 struct pvfs_filename
*name
;
352 /* resolve the cifs name to a posix name */
353 status
= pvfs_resolve_name(pvfs
, req
, info
->generic
.in
.file
.path
, PVFS_RESOLVE_STREAMS
, &name
);
354 if (!NT_STATUS_IS_OK(status
)) {
358 if (!name
->stream_exists
) {
359 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
362 status
= pvfs_can_stat(pvfs
, req
, name
);
363 if (!NT_STATUS_IS_OK(status
)) {
367 status
= pvfs_access_check_simple(pvfs
, req
, name
,
368 pvfs_fileinfo_access(info
));
369 if (!NT_STATUS_IS_OK(status
)) {
373 status
= pvfs_map_fileinfo(pvfs
, req
, name
, info
, -1);
379 query info on a open file
381 NTSTATUS
pvfs_qfileinfo(struct ntvfs_module_context
*ntvfs
,
382 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
384 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
387 struct pvfs_file_handle
*h
;
389 uint32_t access_needed
;
391 f
= pvfs_find_fd(pvfs
, req
, info
->generic
.in
.file
.ntvfs
);
393 return NT_STATUS_INVALID_HANDLE
;
397 access_needed
= pvfs_fileinfo_access(info
);
398 if ((f
->access_mask
& access_needed
) != access_needed
) {
399 return NT_STATUS_ACCESS_DENIED
;
402 /* update the file information */
403 status
= pvfs_resolve_name_handle(pvfs
, h
);
404 if (!NT_STATUS_IS_OK(status
)) {
408 status
= pvfs_map_fileinfo(pvfs
, req
, h
->name
, info
, h
->fd
);
410 /* a qfileinfo can fill in a bit more info than a qpathinfo -
411 now modify the levels that need to be fixed up */
412 switch (info
->generic
.level
) {
413 case RAW_FILEINFO_STANDARD_INFO
:
414 case RAW_FILEINFO_STANDARD_INFORMATION
:
415 if (pvfs_delete_on_close_set(pvfs
, h
)) {
416 info
->standard_info
.out
.delete_pending
= 1;
417 info
->standard_info
.out
.nlink
--;
421 case RAW_FILEINFO_ALL_INFO
:
422 case RAW_FILEINFO_ALL_INFORMATION
:
423 if (pvfs_delete_on_close_set(pvfs
, h
)) {
424 info
->all_info
.out
.delete_pending
= 1;
425 info
->all_info
.out
.nlink
--;
429 case RAW_FILEINFO_POSITION_INFORMATION
:
430 info
->position_information
.out
.position
= h
->position
;
433 case RAW_FILEINFO_ACCESS_INFORMATION
:
434 info
->access_information
.out
.access_flags
= f
->access_mask
;
437 case RAW_FILEINFO_MODE_INFORMATION
:
438 info
->mode_information
.out
.mode
= h
->mode
;
441 case RAW_FILEINFO_SMB2_ALL_INFORMATION
:
442 if (pvfs_delete_on_close_set(pvfs
, h
)) {
443 info
->all_info2
.out
.delete_pending
= 1;
444 info
->all_info2
.out
.nlink
--;
446 info
->all_info2
.out
.position
= h
->position
;
447 info
->all_info2
.out
.access_mask
= f
->access_mask
;
448 info
->all_info2
.out
.mode
= h
->mode
;