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