1/*
2	Copyright 1999-2001, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5/* attributes.c
6 * handles mime type information for ntfs
7 * gets/sets mime information in vnode
8 */
9
10
11#include <KernelExport.h>
12#include <SupportDefs.h>
13#include <TypeConstants.h>
14
15#include <dirent.h>
16#include <fs_attr.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include "attributes.h"
21#include "mime_table.h"
22#include "ntfs.h"
23
24
25const char* kHaikuAttrPrefix={"HAIKU-XATTR:"};
26
27
28status_t
29fs_open_attrib_dir(fs_volume *_vol, fs_vnode *_node, void **_cookie)
30{
31	nspace *ns = (nspace*)_vol->private_volume;
32	vnode *node = (vnode*)_node->private_node;
33	attrdircookie *cookie = NULL;
34	ntfs_inode *ni = NULL;
35	ntfs_attr_search_ctx *ctx = NULL;
36
37	status_t result = B_NO_ERROR;
38
39	TRACE("%s - ENTER\n", __FUNCTION__);
40
41	LOCK_VOL(ns);
42
43	ni = ntfs_inode_open(ns->ntvol, node->vnid);
44	if (ni == NULL) {
45		result = errno;
46		goto exit;
47	}
48
49	ctx = ntfs_attr_get_search_ctx(ni, NULL);
50	if (ctx == NULL) {
51		result = errno;
52		goto exit;
53	}
54
55	cookie = (attrdircookie*)ntfs_calloc(sizeof(attrdircookie));
56	if (cookie == NULL) {
57		result = ENOMEM;
58		goto exit;
59	}
60
61	cookie->inode = ni;
62	cookie->ctx = ctx;
63	ni = NULL;
64	ctx = NULL;
65	*_cookie = cookie;
66
67exit:
68
69	if (ctx != NULL)
70		ntfs_attr_put_search_ctx(ctx);
71	if (ni != NULL)
72		ntfs_inode_close(ni);
73
74	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
75
76	UNLOCK_VOL(ns);
77
78	return result;
79}
80
81
82status_t
83fs_close_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie)
84{
85	return B_NO_ERROR;
86}
87
88
89status_t
90fs_free_attrib_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie)
91{
92	nspace *ns = (nspace *)_vol->private_volume;
93	attrdircookie *cookie = (attrdircookie *)_cookie;
94
95	LOCK_VOL(ns);
96
97	if (cookie->ctx)
98		ntfs_attr_put_search_ctx(cookie->ctx);
99	if (cookie->inode)
100		ntfs_inode_close(cookie->inode);
101
102	UNLOCK_VOL(ns);
103
104	free(cookie);
105	return B_NO_ERROR;
106}
107
108
109status_t
110fs_rewind_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie)
111{
112	nspace *ns = (nspace*)_vol->private_volume;
113	attrdircookie *cookie = (attrdircookie *)_cookie;
114	status_t result = B_NO_ERROR;
115
116	TRACE("%s - ENTER\n", __FUNCTION__);
117
118	LOCK_VOL(ns);
119
120	if (cookie->ctx)
121		ntfs_attr_put_search_ctx(cookie->ctx);
122	cookie->ctx = ntfs_attr_get_search_ctx(cookie->inode, NULL);
123	if (cookie->ctx == NULL) {
124		result = errno;
125		//goto exit;
126	}
127
128//exit:
129
130	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
131
132	UNLOCK_VOL(ns);
133
134	return result;
135}
136
137
138status_t
139fs_read_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie,
140	struct dirent *entry, size_t bufsize, uint32 *num)
141{
142	nspace *ns = (nspace *)_vol->private_volume;
143	vnode *node = (vnode *)_node->private_node;
144	char *name = NULL;
145	attrdircookie *cookie = (attrdircookie *)_cookie;
146	uint32 numEntries = 0;
147	status_t result = B_NO_ERROR;
148
149	if (cookie->ctx == NULL)
150		panic("cookie->ctx == NULL");
151
152	LOCK_VOL(ns);
153
154	TRACE("%s - ENTER\n", __FUNCTION__);
155
156	while (!(result = ntfs_attrs_walk(cookie->ctx))) {
157		ATTR_RECORD *attr = cookie->ctx->attr;
158		if (attr->type == AT_DATA) {
159			const char *real_name;
160			// it's the actual file body
161			if (attr->name_length == 0)
162				continue;
163
164			name = ntfs_attr_name_get((const ntfschar *)(((char *)attr)
165				+ attr->name_offset), attr->name_length);
166
167			if(strncmp(name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 ) {
168				TRACE("found AT_DATA '%s' - Skip\n", name);
169				continue;
170			}
171			TRACE("found AT_DATA '%s' - Found\n", name);
172
173			real_name = name + strlen(kHaikuAttrPrefix);
174
175			bufsize = MIN(bufsize, sizeof(struct dirent) + strlen(real_name) + 1);
176			entry->d_ino = node->vnid;
177			entry->d_dev = ns->id;
178			entry->d_reclen = sizeof(struct dirent) + strlen(real_name);
179			//XXX size
180			strcpy(entry->d_name, real_name);
181			ntfs_attr_name_free(&name);
182			numEntries++;
183			if (numEntries >= *num)
184				break;
185			break;
186		}
187	}
188	if (result && errno != ENOENT) {
189		result = errno;
190		goto exit;
191	} else
192		result = B_OK;
193
194exit:
195
196	TRACE("%s - EXIT, result is %s, *num %d\n", __FUNCTION__,
197		strerror(result), *num);
198
199	UNLOCK_VOL(ns);
200
201	if (result)
202		*num = 0;
203	else
204		*num = numEntries;
205
206	return result;
207}
208
209
210status_t
211fs_create_attrib(fs_volume *_vol, fs_vnode *_node, const char* name,
212	uint32 type, int openMode, void** _cookie)
213{
214	nspace *ns = (nspace*)_vol->private_volume;
215	vnode *node = (vnode*)_node->private_node;
216	attrcookie *cookie = NULL;
217	ntfschar *uname = NULL;
218	int ulen;
219	ntfs_inode *ni = NULL;
220	ntfs_attr *na = NULL;
221	status_t result = B_NO_ERROR;
222
223	if (ns->flags & B_FS_IS_READONLY) {
224		return B_READ_ONLY_DEVICE;
225	}
226
227	TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name, node->vnid);
228
229	LOCK_VOL(ns);
230
231	if (node == NULL) {
232		result = EINVAL;
233		goto exit;
234	}
235
236	ni = ntfs_inode_open(ns->ntvol, node->vnid);
237	if (ni == NULL) {
238		result = errno;
239		TRACE("%s - inode_open: %s\n", __FUNCTION__, strerror(result));
240		goto exit;
241	}
242
243	// UXA demangling TODO
244
245	// check for EA first... TODO: WRITEME
246
247
248	// check for a named stream
249	if (true) {
250		char ntfs_attr_name[MAX_PATH] = {0};
251		strcat(ntfs_attr_name, kHaikuAttrPrefix);
252		strcat(ntfs_attr_name,name);
253
254		uname = ntfs_calloc(MAX_PATH);
255		ulen = ntfs_mbstoucs(ntfs_attr_name, &uname);
256		if (ulen < 0) {
257			result = EILSEQ;
258			TRACE("%s - mb alloc: %s\n", __FUNCTION__, strerror(result));
259			goto exit;
260		}
261
262		na = ntfs_attr_open(ni, AT_DATA, uname, ulen);
263		if (na != NULL) {
264			if (ntfs_attr_truncate(na, 0)) {
265				result = errno;
266				goto exit;
267			}
268		} else {
269			if (ntfs_attr_add(ni, AT_DATA, uname, ulen, NULL, 0) < 0) {
270				result = errno;
271				TRACE("%s - ntfs_attr_add: %s\n", __FUNCTION__,
272					strerror(result));
273				goto exit;
274			}
275			na = ntfs_attr_open(ni, AT_DATA, uname, ulen);
276			if (na == NULL) {
277				result = errno;
278				TRACE("%s - ntfs_attr_open: %s\n", __FUNCTION__,
279					strerror(result));
280				goto exit;
281			}
282		}
283		if(ntfs_attr_pwrite(na, 0, sizeof(uint32), &type) < 0 ) {
284			result = errno;
285			goto exit;
286		}
287	}
288
289	cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie));
290
291	if (cookie != NULL) {
292		cookie->omode = openMode;
293		*_cookie = (void*)cookie;
294		cookie->vnid = node->vnid;
295		cookie->uname = uname;
296		cookie->uname_len = ulen;
297		cookie->type = type;
298		uname = NULL;
299	} else
300		result = ENOMEM;
301
302exit:
303	if (uname != NULL)
304		free(uname);
305
306	if (na != NULL)
307		ntfs_attr_close(na);
308
309	if (ni != NULL)
310		ntfs_inode_close(ni);
311
312	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
313
314	UNLOCK_VOL(ns);
315
316	return result;
317}
318
319
320status_t
321fs_open_attrib(fs_volume *_vol, fs_vnode *_node, const char *name,
322	int openMode, void **_cookie)
323{
324	nspace *ns = (nspace*)_vol->private_volume;
325	vnode *node = (vnode*)_node->private_node;
326	attrcookie *cookie = NULL;
327	ntfschar *uname = NULL;
328	int ulen;
329	ntfs_inode *ni = NULL;
330	ntfs_attr *na = NULL;
331	status_t result = B_NO_ERROR;
332	uint32	type = B_XATTR_TYPE;
333
334	TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name,  node->vnid);
335
336	LOCK_VOL(ns);
337
338	if (node == NULL) {
339		result = EINVAL;
340		goto exit;
341	}
342
343	ni = ntfs_inode_open(ns->ntvol, node->vnid);
344	if (ni == NULL) {
345		result = errno;
346		goto exit;
347	}
348
349	// UXA demangling TODO
350
351	// check for EA first... TODO: WRITEME
352
353
354	// check for a named stream
355	if (true) {
356		char ntfs_attr_name[MAX_PATH] = {0};
357		strcat(ntfs_attr_name, kHaikuAttrPrefix);
358		strcat(ntfs_attr_name, name);
359
360		uname = ntfs_calloc(MAX_PATH);
361		ulen = ntfs_mbstoucs(ntfs_attr_name, &uname);
362		if (ulen < 0) {
363			result = EILSEQ;
364			goto exit;
365		}
366
367		na = ntfs_attr_open(ni, AT_DATA, uname, ulen);
368		if (na != NULL) {
369			if (openMode & O_TRUNC) {
370				if (ns->flags & B_FS_IS_READONLY) {
371					result = B_READ_ONLY_DEVICE;
372					goto exit;
373				} else {
374					if (ntfs_attr_truncate(na, sizeof(uint32))) {
375						result = errno;
376						goto exit;
377					}
378				}
379			}
380			if (ntfs_attr_pread(na, 0, sizeof(uint32), &type) != sizeof(uint32)) {
381				result = errno;
382				goto exit;
383			}
384		} else {
385			result = ENOENT;
386			goto exit;
387		}
388	}
389
390
391	cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie));
392
393	if (cookie != NULL) {
394		cookie->omode = openMode;
395		cookie->vnid = node->vnid;
396		cookie->uname = uname;
397		cookie->uname_len = ulen;
398		cookie->type = type;
399		*_cookie = (void*)cookie;
400		uname = NULL;
401	} else
402		result = ENOMEM;
403
404exit:
405	if (uname != NULL)
406		free(uname);
407
408	if (na != NULL)
409		ntfs_attr_close(na);
410
411	if (ni != NULL)
412		ntfs_inode_close(ni);
413
414	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
415
416	UNLOCK_VOL(ns);
417
418	return result;
419}
420
421
422status_t
423fs_close_attrib(fs_volume *_vol, fs_vnode *_node, void *cookie)
424{
425	TRACE("%s vnid: %d\n", __FUNCTION__, ((vnode*)_node->private_node)->vnid);
426	return B_NO_ERROR;
427}
428
429
430status_t
431fs_free_attrib_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie)
432{
433	nspace *ns = (nspace*)_vol->private_volume;
434	attrcookie *cookie = (attrcookie *)_cookie;
435
436	LOCK_VOL(ns);
437
438	if (cookie->uname != NULL)
439		free(cookie->uname);
440
441	UNLOCK_VOL(ns);
442
443	free(cookie);
444	return B_NO_ERROR;
445}
446
447
448status_t
449fs_read_attrib_stat(fs_volume *_vol, fs_vnode *_node, void *_cookie,
450	struct stat *stat)
451{
452	nspace *ns = (nspace *)_vol->private_volume;
453	vnode *node = (vnode *)_node->private_node;
454	attrcookie *cookie = (attrcookie *)_cookie;
455	ntfs_inode *ni = NULL;
456	ntfs_attr *na = NULL;
457	status_t result = B_NO_ERROR;
458
459	LOCK_VOL(ns);
460
461	ni = ntfs_inode_open(ns->ntvol, node->vnid);
462	if (ni == NULL) {
463		result = errno;
464		goto exit;
465	}
466	na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len);
467	if (na == NULL) {
468		result = errno;
469		goto exit;
470	}
471
472	stat->st_type = cookie->type;
473	stat->st_size = na ? na->data_size - sizeof(uint32) : 0;
474
475exit:
476	if (na != NULL)
477		ntfs_attr_close(na);
478	if (ni != NULL)
479		ntfs_inode_close(ni);
480
481	UNLOCK_VOL(ns);
482
483	return B_NO_ERROR;
484}
485
486
487status_t
488fs_read_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
489	void *buffer, size_t *len)
490{
491	nspace *ns = (nspace *)_vol->private_volume;
492	vnode *node = (vnode *)_node->private_node;
493	attrcookie *cookie = (attrcookie *)_cookie;
494	ntfs_inode *ni = NULL;
495	ntfs_attr *na = NULL;
496	size_t size = *len;
497	int total = 0;
498	status_t result = B_NO_ERROR;
499
500	if (pos < 0) {
501		*len = 0;
502		return EINVAL;
503	}
504
505	LOCK_VOL(ns);
506
507	TRACE("%s - ENTER  vnid: %d\n", __FUNCTION__, node->vnid);
508
509	ni = ntfs_inode_open(ns->ntvol, node->vnid);
510	if (ni == NULL) {
511		result = errno;
512		goto exit;
513	}
514	na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len);
515	if (na == NULL) {
516		result = errno;
517		goto exit;
518	}
519
520	pos += sizeof(uint32);
521
522	// it is a named stream
523	if (na != NULL) {
524		if (pos + size > na->data_size)
525			size = na->data_size - pos;
526
527		while (size) {
528			off_t bytesRead = ntfs_attr_pread(na, pos, size, buffer);
529			if (bytesRead < (s64)size) {
530				ERROR("ntfs_attr_pread returned less bytes than "
531					"requested.\n");
532			}
533			if (bytesRead <= 0) {
534				*len = 0;
535				result = EINVAL;
536				goto exit;
537			}
538			size -= bytesRead;
539			pos += bytesRead;
540			total += bytesRead;
541		}
542
543		*len = total;
544	} else {
545		*len = 0;
546		result = ENOENT; // TODO
547	}
548
549exit:
550	if (na != NULL)
551		ntfs_attr_close(na);
552	if (ni != NULL)
553		ntfs_inode_close(ni);
554
555	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
556
557	UNLOCK_VOL(ns);
558
559	return result;
560}
561
562
563status_t
564fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
565	const void *buffer, size_t *_length)
566{
567	nspace *ns = (nspace *)_vol->private_volume;
568	vnode *node = (vnode *)_node->private_node;
569	attrcookie *cookie = (attrcookie *)_cookie;
570	ntfs_inode *ni = NULL;
571	ntfs_attr *na = NULL;
572	size_t  size = *_length;
573	char *attr_name = NULL;
574	char *real_name = NULL;
575	int total = 0;
576	status_t result = B_NO_ERROR;
577
578	TRACE("%s - ENTER  vnode: %d\n", __FUNCTION__, node->vnid);
579
580	if (ns->flags & B_FS_IS_READONLY) {
581		return B_READ_ONLY_DEVICE;
582	}
583
584	if (pos < 0) {
585		*_length = 0;
586		return EINVAL;
587	}
588
589	LOCK_VOL(ns);
590
591	ni = ntfs_inode_open(ns->ntvol, node->vnid);
592	if (ni == NULL) {
593		result = errno;
594		goto exit;
595	}
596	na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len);
597	if (na == NULL) {
598		result = errno;
599		goto exit;
600	}
601
602	pos += sizeof(uint32);
603
604	// it is a named stream
605	if (na != NULL) {
606		if (cookie->omode & O_APPEND)
607			pos = na->data_size;
608
609		if (pos + size > na->data_size) {
610			ntfs_mark_free_space_outdated(ns);
611			if (ntfs_attr_truncate(na, pos + size))
612				size = na->data_size - pos;
613			else
614				notify_stat_changed(ns->id, MREF(ni->mft_no), B_STAT_SIZE);
615		}
616
617		while (size) {
618			off_t bytesWritten = ntfs_attr_pwrite(na, pos, size, buffer);
619			if (bytesWritten < (s64)size)
620				ERROR("%s - ntfs_attr_pwrite returned less bytes than "
621					"requested.\n", __FUNCTION__);
622			if (bytesWritten <= 0) {
623				ERROR("%s - ntfs_attr_pwrite()<=0\n", __FUNCTION__);
624				*_length = 0;
625				result = EINVAL;
626				goto exit;
627			}
628			size -= bytesWritten;
629			pos += bytesWritten;
630			total += bytesWritten;
631		}
632
633		*_length = total;
634	} else {
635		*_length = 0;
636		result =  EINVAL;
637		goto exit;
638	}
639
640	if (ntfs_ucstombs(na->name, na->name_len, &attr_name, 0) >= 0) {
641		if (attr_name != NULL) {
642			if(strncmp(attr_name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 )
643				goto exit;
644			real_name = attr_name + strlen(kHaikuAttrPrefix);
645			notify_attribute_changed(ns->id, MREF(ni->mft_no),
646				real_name, B_ATTR_CHANGED);
647			free(attr_name);
648		}
649	}
650
651exit:
652	if (na != NULL)
653		ntfs_attr_close(na);
654	if (ni != NULL)
655		ntfs_inode_close(ni);
656
657	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
658
659	UNLOCK_VOL(ns);
660
661	return result;
662}
663
664
665status_t
666fs_remove_attrib(fs_volume *_vol, fs_vnode *_node, const char* name)
667{
668	nspace *ns = (nspace *)_vol->private_volume;
669	vnode *node = (vnode *)_node->private_node;
670	char ntfs_attr_name[MAX_PATH]={0};
671	ntfschar *uname = NULL;
672	int ulen;
673	ntfs_inode *ni = NULL;
674	status_t result = B_NO_ERROR;
675
676	TRACE("%s - ENTER - name: [%s]\n", __FUNCTION__, name);
677
678	if (ns->flags & B_FS_IS_READONLY) {
679		ERROR("ntfs is read-only\n");
680		return B_READ_ONLY_DEVICE;
681	}
682
683	LOCK_VOL(ns);
684
685	if (node == NULL) {
686		result = EINVAL;
687		goto exit;
688	}
689
690	ni = ntfs_inode_open(ns->ntvol, node->vnid);
691	if (ni == NULL) {
692		result = errno;
693		goto exit;
694	}
695
696	strcat(ntfs_attr_name, kHaikuAttrPrefix);
697	strcat(ntfs_attr_name, name);
698
699	uname = ntfs_calloc(MAX_PATH);
700	ulen = ntfs_mbstoucs(ntfs_attr_name, &uname);
701	if (ulen < 0) {
702		result = EILSEQ;
703		goto exit;
704	}
705
706	if (ntfs_attr_remove(ni, AT_DATA, uname, ulen)) {
707		result = ENOENT;
708		goto exit;
709	}
710
711	if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
712		ni->flags |= FILE_ATTR_ARCHIVE;
713		NInoFileNameSetDirty(ni);
714	}
715	notify_attribute_changed(ns->id, MREF(ni->mft_no), name, B_ATTR_REMOVED);
716exit:
717	if (uname != NULL)
718		free(uname);
719
720	if (ni != NULL)
721		ntfs_inode_close(ni);
722
723	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
724
725	UNLOCK_VOL(ns);
726
727	return result;
728}
729