1/*
2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2018, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8//! Operations on file descriptors
9
10
11#include <fd.h>
12
13#include <stdlib.h>
14#include <string.h>
15
16#include <OS.h>
17
18#include <AutoDeleter.h>
19#include <AutoDeleterDrivers.h>
20#include <BytePointer.h>
21
22#include <syscalls.h>
23#include <syscall_restart.h>
24#include <slab/Slab.h>
25#include <util/AutoLock.h>
26#include <util/iovec_support.h>
27#include <vfs.h>
28#include <wait_for_objects.h>
29
30#include "vfs_tracing.h"
31
32
33//#define TRACE_FD
34#ifdef TRACE_FD
35#	define TRACE(x) dprintf x
36#else
37#	define TRACE(x)
38#endif
39
40
41static const size_t kMaxReadDirBufferSize = 64 * 1024;
42
43extern object_cache* sFileDescriptorCache;
44
45
46static struct file_descriptor* get_fd_locked(struct io_context* context,
47	int fd);
48static struct file_descriptor* remove_fd(struct io_context* context, int fd);
49static void deselect_select_infos(file_descriptor* descriptor,
50	select_info* infos, bool putSyncObjects);
51
52
53//	#pragma mark - General fd routines
54
55
56#ifdef DEBUG
57void dump_fd(int fd, struct file_descriptor* descriptor);
58
59void
60dump_fd(int fd,struct file_descriptor* descriptor)
61{
62	dprintf("fd[%d] = %p: type = %" B_PRId32 ", ref_count = %" B_PRId32 ", ops "
63		"= %p, u.vnode = %p, u.mount = %p, cookie = %p, open_mode = %" B_PRIx32
64		", pos = %" B_PRId64 "\n",
65		fd, descriptor, descriptor->type, descriptor->ref_count,
66		descriptor->ops, descriptor->u.vnode, descriptor->u.mount,
67		descriptor->cookie, descriptor->open_mode, descriptor->pos);
68}
69#endif
70
71
72/*! Allocates and initializes a new file_descriptor.
73*/
74struct file_descriptor*
75alloc_fd(void)
76{
77	file_descriptor* descriptor
78		= (file_descriptor*)object_cache_alloc(sFileDescriptorCache, 0);
79	if (descriptor == NULL)
80		return NULL;
81
82	descriptor->u.vnode = NULL;
83	descriptor->cookie = NULL;
84	descriptor->ref_count = 1;
85	descriptor->open_count = 0;
86	descriptor->open_mode = 0;
87	descriptor->pos = -1;
88
89	return descriptor;
90}
91
92
93bool
94fd_close_on_exec(struct io_context* context, int fd)
95{
96	return CHECK_BIT(context->fds_close_on_exec[fd / 8], fd & 7) ? true : false;
97}
98
99
100void
101fd_set_close_on_exec(struct io_context* context, int fd, bool closeFD)
102{
103	if (closeFD)
104		context->fds_close_on_exec[fd / 8] |= (1 << (fd & 7));
105	else
106		context->fds_close_on_exec[fd / 8] &= ~(1 << (fd & 7));
107}
108
109
110/*!	Searches a free slot in the FD table of the provided I/O context, and
111	inserts the specified descriptor into it.
112*/
113int
114new_fd_etc(struct io_context* context, struct file_descriptor* descriptor,
115	int firstIndex)
116{
117	int fd = -1;
118	uint32 i;
119
120	if (firstIndex < 0 || (uint32)firstIndex >= context->table_size)
121		return B_BAD_VALUE;
122
123	mutex_lock(&context->io_mutex);
124
125	for (i = firstIndex; i < context->table_size; i++) {
126		if (!context->fds[i]) {
127			fd = i;
128			break;
129		}
130	}
131	if (fd < 0) {
132		fd = B_NO_MORE_FDS;
133		goto err;
134	}
135
136	TFD(NewFD(context, fd, descriptor));
137
138	context->fds[fd] = descriptor;
139	context->num_used_fds++;
140	atomic_add(&descriptor->open_count, 1);
141
142err:
143	mutex_unlock(&context->io_mutex);
144
145	return fd;
146}
147
148
149int
150new_fd(struct io_context* context, struct file_descriptor* descriptor)
151{
152	return new_fd_etc(context, descriptor, 0);
153}
154
155
156/*!	Reduces the descriptor's reference counter, and frees all resources
157	when it's no longer used.
158*/
159void
160put_fd(struct file_descriptor* descriptor)
161{
162	int32 previous = atomic_add(&descriptor->ref_count, -1);
163
164	TFD(PutFD(descriptor));
165
166	TRACE(("put_fd(descriptor = %p [ref = %" B_PRId32 ", cookie = %p])\n",
167		descriptor, descriptor->ref_count, descriptor->cookie));
168
169	// free the descriptor if we don't need it anymore
170	if (previous == 1) {
171		// free the underlying object
172		if (descriptor->ops != NULL && descriptor->ops->fd_free != NULL)
173			descriptor->ops->fd_free(descriptor);
174
175		object_cache_free(sFileDescriptorCache, descriptor, 0);
176	} else if ((descriptor->open_mode & O_DISCONNECTED) != 0
177		&& previous - 1 == descriptor->open_count
178		&& descriptor->ops != NULL) {
179		// the descriptor has been disconnected - it cannot
180		// be accessed anymore, let's close it (no one is
181		// currently accessing this descriptor)
182
183		if (descriptor->ops->fd_close)
184			descriptor->ops->fd_close(descriptor);
185		if (descriptor->ops->fd_free)
186			descriptor->ops->fd_free(descriptor);
187
188		// prevent this descriptor from being closed/freed again
189		descriptor->ops = NULL;
190		descriptor->u.vnode = NULL;
191
192		// the file descriptor is kept intact, so that it's not
193		// reused until someone explicitly closes it
194	}
195}
196
197
198/*!	Decrements the open counter of the file descriptor and invokes
199	its close hook when appropriate.
200*/
201void
202close_fd(struct io_context* context, struct file_descriptor* descriptor)
203{
204	// POSIX advisory locks need to be released when any file descriptor closes
205	if (descriptor->type == FDTYPE_FILE)
206		vfs_release_posix_lock(context, descriptor);
207
208	if (atomic_add(&descriptor->open_count, -1) == 1) {
209		vfs_unlock_vnode_if_locked(descriptor);
210
211		if (descriptor->ops != NULL && descriptor->ops->fd_close != NULL)
212			descriptor->ops->fd_close(descriptor);
213	}
214}
215
216
217status_t
218close_fd_index(struct io_context* context, int fd)
219{
220	struct file_descriptor* descriptor = remove_fd(context, fd);
221
222	if (descriptor == NULL)
223		return B_FILE_ERROR;
224
225	close_fd(context, descriptor);
226	put_fd(descriptor);
227		// the reference associated with the slot
228
229	return B_OK;
230}
231
232
233/*!	This descriptor's underlying object will be closed and freed as soon as
234	possible (in one of the next calls to put_fd() - get_fd() will no longer
235	succeed on this descriptor).
236	This is useful if the underlying object is gone, for instance when a
237	(mounted) volume got removed unexpectedly.
238*/
239void
240disconnect_fd(struct file_descriptor* descriptor)
241{
242	descriptor->open_mode |= O_DISCONNECTED;
243}
244
245
246void
247inc_fd_ref_count(struct file_descriptor* descriptor)
248{
249	atomic_add(&descriptor->ref_count, 1);
250}
251
252
253static struct file_descriptor*
254get_fd_locked(struct io_context* context, int fd)
255{
256	if (fd < 0 || (uint32)fd >= context->table_size)
257		return NULL;
258
259	struct file_descriptor* descriptor = context->fds[fd];
260
261	if (descriptor != NULL) {
262		// disconnected descriptors cannot be accessed anymore
263		if (descriptor->open_mode & O_DISCONNECTED)
264			return NULL;
265
266		TFD(GetFD(context, fd, descriptor));
267		inc_fd_ref_count(descriptor);
268	}
269
270	return descriptor;
271}
272
273
274struct file_descriptor*
275get_fd(struct io_context* context, int fd)
276{
277	MutexLocker _(context->io_mutex);
278
279	return get_fd_locked(context, fd);
280}
281
282
283struct file_descriptor*
284get_open_fd(struct io_context* context, int fd)
285{
286	MutexLocker _(context->io_mutex);
287
288	file_descriptor* descriptor = get_fd_locked(context, fd);
289	if (descriptor == NULL)
290		return NULL;
291
292	atomic_add(&descriptor->open_count, 1);
293
294	return descriptor;
295}
296
297
298/*!	Removes the file descriptor from the specified slot.
299*/
300static struct file_descriptor*
301remove_fd(struct io_context* context, int fd)
302{
303	struct file_descriptor* descriptor = NULL;
304
305	if (fd < 0)
306		return NULL;
307
308	mutex_lock(&context->io_mutex);
309
310	if ((uint32)fd < context->table_size)
311		descriptor = context->fds[fd];
312
313	select_info* selectInfos = NULL;
314	bool disconnected = false;
315
316	if (descriptor != NULL)	{
317		// fd is valid
318		TFD(RemoveFD(context, fd, descriptor));
319
320		context->fds[fd] = NULL;
321		fd_set_close_on_exec(context, fd, false);
322		context->num_used_fds--;
323
324		selectInfos = context->select_infos[fd];
325		context->select_infos[fd] = NULL;
326
327		disconnected = (descriptor->open_mode & O_DISCONNECTED);
328	}
329
330	if (selectInfos != NULL)
331		deselect_select_infos(descriptor, selectInfos, true);
332
333	mutex_unlock(&context->io_mutex);
334
335	return disconnected ? NULL : descriptor;
336}
337
338
339static int
340dup_fd(int fd, bool kernel)
341{
342	struct io_context* context = get_current_io_context(kernel);
343	struct file_descriptor* descriptor;
344	int status;
345
346	TRACE(("dup_fd: fd = %d\n", fd));
347
348	// Try to get the fd structure
349	descriptor = get_fd(context, fd);
350	if (descriptor == NULL)
351		return B_FILE_ERROR;
352
353	// now put the fd in place
354	status = new_fd(context, descriptor);
355	if (status < 0)
356		put_fd(descriptor);
357	else {
358		mutex_lock(&context->io_mutex);
359		fd_set_close_on_exec(context, status, false);
360		mutex_unlock(&context->io_mutex);
361	}
362
363	return status;
364}
365
366
367/*!	POSIX says this should be the same as:
368		close(newfd);
369		fcntl(oldfd, F_DUPFD, newfd);
370
371	We do dup2() directly to be thread-safe.
372*/
373static int
374dup2_fd(int oldfd, int newfd, bool kernel)
375{
376	struct file_descriptor* evicted = NULL;
377	struct io_context* context;
378
379	TRACE(("dup2_fd: ofd = %d, nfd = %d\n", oldfd, newfd));
380
381	// quick check
382	if (oldfd < 0 || newfd < 0)
383		return B_FILE_ERROR;
384
385	// Get current I/O context and lock it
386	context = get_current_io_context(kernel);
387	mutex_lock(&context->io_mutex);
388
389	// Check if the fds are valid (mutex must be locked because
390	// the table size could be changed)
391	if ((uint32)oldfd >= context->table_size
392		|| (uint32)newfd >= context->table_size
393		|| context->fds[oldfd] == NULL
394		|| (context->fds[oldfd]->open_mode & O_DISCONNECTED) != 0) {
395		mutex_unlock(&context->io_mutex);
396		return B_FILE_ERROR;
397	}
398
399	// Check for identity, note that it cannot be made above
400	// because we always want to return an error on invalid
401	// handles
402	if (oldfd != newfd) {
403		// Now do the work
404		TFD(Dup2FD(context, oldfd, newfd));
405
406		evicted = context->fds[newfd];
407		select_info* selectInfos = context->select_infos[newfd];
408		context->select_infos[newfd] = NULL;
409		atomic_add(&context->fds[oldfd]->ref_count, 1);
410		atomic_add(&context->fds[oldfd]->open_count, 1);
411		context->fds[newfd] = context->fds[oldfd];
412
413		if (evicted == NULL)
414			context->num_used_fds++;
415
416		deselect_select_infos(evicted, selectInfos, true);
417	}
418
419	fd_set_close_on_exec(context, newfd, false);
420
421	mutex_unlock(&context->io_mutex);
422
423	// Say bye bye to the evicted fd
424	if (evicted) {
425		close_fd(context, evicted);
426		put_fd(evicted);
427	}
428
429	return newfd;
430}
431
432
433/*!	Duplicates an FD from another team to this/the kernel team.
434	\param fromTeam The team which owns the FD.
435	\param fd The FD to duplicate.
436	\param kernel If \c true, the new FD will be created in the kernel team,
437			the current userland team otherwise.
438	\return The newly created FD or an error code, if something went wrong.
439*/
440int
441dup_foreign_fd(team_id fromTeam, int fd, bool kernel)
442{
443	// get the I/O context for the team in question
444	Team* team = Team::Get(fromTeam);
445	if (team == NULL)
446		return B_BAD_TEAM_ID;
447	BReference<Team> teamReference(team, true);
448
449	io_context* fromContext = team->io_context;
450
451	// get the file descriptor
452	file_descriptor* descriptor = get_fd(fromContext, fd);
453	if (descriptor == NULL)
454		return B_FILE_ERROR;
455	FileDescriptorPutter descriptorPutter(descriptor);
456
457	// create a new FD in the target I/O context
458	int result = new_fd(get_current_io_context(kernel), descriptor);
459	if (result >= 0) {
460		// the descriptor reference belongs to the slot, now
461		descriptorPutter.Detach();
462	}
463
464	return result;
465}
466
467
468static status_t
469fd_ioctl(bool kernelFD, int fd, uint32 op, void* buffer, size_t length)
470{
471	FileDescriptorPutter descriptor(get_fd(get_current_io_context(kernelFD), fd));
472	if (!descriptor.IsSet())
473		return B_FILE_ERROR;
474
475	status_t status;
476	if (descriptor->ops->fd_ioctl)
477		status = descriptor->ops->fd_ioctl(descriptor.Get(), op, buffer, length);
478	else
479		status = B_DEV_INVALID_IOCTL;
480
481	if (status == B_DEV_INVALID_IOCTL)
482		status = ENOTTY;
483
484	return status;
485}
486
487
488static void
489deselect_select_infos(file_descriptor* descriptor, select_info* infos,
490	bool putSyncObjects)
491{
492	TRACE(("deselect_select_infos(%p, %p)\n", descriptor, infos));
493
494	select_info* info = infos;
495	while (info != NULL) {
496		select_sync* sync = info->sync;
497
498		// deselect the selected events
499		uint16 eventsToDeselect = info->selected_events & ~B_EVENT_INVALID;
500		if (descriptor->ops->fd_deselect != NULL && eventsToDeselect != 0) {
501			for (uint16 event = 1; event < 16; event++) {
502				if ((eventsToDeselect & SELECT_FLAG(event)) != 0) {
503					descriptor->ops->fd_deselect(descriptor, event,
504						(selectsync*)info);
505				}
506			}
507		}
508
509		select_info* next = info->next;
510		notify_select_events(info, B_EVENT_INVALID);
511		info = next;
512
513		if (putSyncObjects)
514			put_select_sync(sync);
515	}
516}
517
518
519status_t
520select_fd(int32 fd, struct select_info* info, bool kernel)
521{
522	TRACE(("select_fd(fd = %" B_PRId32 ", info = %p (%p), 0x%x)\n", fd, info,
523		info->sync, info->selected_events));
524
525	FileDescriptorPutter descriptor;
526		// define before the context locker, so it will be destroyed after it
527
528	io_context* context = get_current_io_context(kernel);
529	MutexLocker locker(context->io_mutex);
530
531	descriptor.SetTo(get_fd_locked(context, fd));
532	if (!descriptor.IsSet())
533		return B_FILE_ERROR;
534
535	uint16 eventsToSelect = info->selected_events & ~B_EVENT_INVALID;
536
537	if (descriptor->ops->fd_select == NULL) {
538		// if the I/O subsystem doesn't support select(), we will
539		// immediately notify the select call
540		eventsToSelect &= ~SELECT_OUTPUT_ONLY_FLAGS;
541		if (eventsToSelect != 0)
542			return notify_select_events(info, eventsToSelect);
543		else
544			return B_OK;
545	}
546
547	// We need the FD to stay open while we're doing this, so no select()/
548	// deselect() will be called on it after it is closed.
549	atomic_add(&descriptor->open_count, 1);
550
551	locker.Unlock();
552
553	// select any events asked for
554	uint32 selectedEvents = 0;
555
556	for (uint16 event = 1; event < 16; event++) {
557		if ((eventsToSelect & SELECT_FLAG(event)) != 0
558			&& descriptor->ops->fd_select(descriptor.Get(), event,
559				(selectsync*)info) == B_OK) {
560			selectedEvents |= SELECT_FLAG(event);
561		}
562	}
563	info->selected_events = selectedEvents
564		| (info->selected_events & B_EVENT_INVALID);
565
566	// Add the info to the IO context. Even if nothing has been selected -- we
567	// always support B_EVENT_INVALID.
568	locker.Lock();
569	if (context->fds[fd] != descriptor.Get()) {
570		// Someone close()d the index in the meantime. deselect() all
571		// events.
572		info->next = NULL;
573		deselect_select_infos(descriptor.Get(), info, false);
574
575		// Release our open reference of the descriptor.
576		close_fd(context, descriptor.Get());
577		return B_FILE_ERROR;
578	}
579
580	// The FD index hasn't changed, so we add the select info to the table.
581
582	info->next = context->select_infos[fd];
583	context->select_infos[fd] = info;
584
585	// As long as the info is in the list, we keep a reference to the sync
586	// object.
587	acquire_select_sync(info->sync);
588
589	// Finally release our open reference. It is safe just to decrement,
590	// since as long as the descriptor is associated with the slot,
591	// someone else still has it open.
592	atomic_add(&descriptor->open_count, -1);
593
594	return B_OK;
595}
596
597
598status_t
599deselect_fd(int32 fd, struct select_info* info, bool kernel)
600{
601	TRACE(("deselect_fd(fd = %" B_PRId32 ", info = %p (%p), 0x%x)\n", fd, info,
602		info->sync, info->selected_events));
603
604	FileDescriptorPutter descriptor;
605		// define before the context locker, so it will be destroyed after it
606
607	io_context* context = get_current_io_context(kernel);
608	MutexLocker locker(context->io_mutex);
609
610	descriptor.SetTo(get_fd_locked(context, fd));
611	if (!descriptor.IsSet())
612		return B_FILE_ERROR;
613
614	// remove the info from the IO context
615
616	select_info** infoLocation = &context->select_infos[fd];
617	while (*infoLocation != NULL && *infoLocation != info)
618		infoLocation = &(*infoLocation)->next;
619
620	// If not found, someone else beat us to it.
621	if (*infoLocation != info)
622		return B_OK;
623
624	*infoLocation = info->next;
625
626	locker.Unlock();
627
628	// deselect the selected events
629	uint16 eventsToDeselect = info->selected_events & ~B_EVENT_INVALID;
630	if (descriptor->ops->fd_deselect != NULL && eventsToDeselect != 0) {
631		for (uint16 event = 1; event < 16; event++) {
632			if ((eventsToDeselect & SELECT_FLAG(event)) != 0) {
633				descriptor->ops->fd_deselect(descriptor.Get(), event,
634					(selectsync*)info);
635			}
636		}
637	}
638
639	put_select_sync(info->sync);
640
641	return B_OK;
642}
643
644
645/*!	This function checks if the specified fd is valid in the current
646	context. It can be used for a quick check; the fd is not locked
647	so it could become invalid immediately after this check.
648*/
649bool
650fd_is_valid(int fd, bool kernel)
651{
652	struct file_descriptor* descriptor
653		= get_fd(get_current_io_context(kernel), fd);
654	if (descriptor == NULL)
655		return false;
656
657	put_fd(descriptor);
658	return true;
659}
660
661
662struct vnode*
663fd_vnode(struct file_descriptor* descriptor)
664{
665	switch (descriptor->type) {
666		case FDTYPE_FILE:
667		case FDTYPE_DIR:
668		case FDTYPE_ATTR_DIR:
669		case FDTYPE_ATTR:
670			return descriptor->u.vnode;
671	}
672
673	return NULL;
674}
675
676
677static status_t
678common_close(int fd, bool kernel)
679{
680	return close_fd_index(get_current_io_context(kernel), fd);
681}
682
683
684static ssize_t
685common_user_io(int fd, off_t pos, void* buffer, size_t length, bool write)
686{
687	if (pos < -1)
688		return B_BAD_VALUE;
689
690	FileDescriptorPutter descriptor(get_fd(get_current_io_context(false), fd));
691	if (!descriptor.IsSet())
692		return B_FILE_ERROR;
693
694	if (write ? (descriptor->open_mode & O_RWMASK) == O_RDONLY
695			: (descriptor->open_mode & O_RWMASK) == O_WRONLY) {
696		return B_FILE_ERROR;
697	}
698
699	bool movePosition = false;
700	if (pos == -1 && descriptor->pos != -1) {
701		pos = descriptor->pos;
702		movePosition = true;
703	}
704
705	if (write ? descriptor->ops->fd_write == NULL
706			: descriptor->ops->fd_read == NULL) {
707		return B_BAD_VALUE;
708	}
709
710	if (length == 0)
711		return 0;
712
713	if (!is_user_address_range(buffer, length))
714		return B_BAD_ADDRESS;
715
716	SyscallRestartWrapper<status_t> status;
717
718	if (write)
719		status = descriptor->ops->fd_write(descriptor.Get(), pos, buffer, &length);
720	else
721		status = descriptor->ops->fd_read(descriptor.Get(), pos, buffer, &length);
722
723	if (status != B_OK)
724		return status;
725
726	if (movePosition) {
727		descriptor->pos = write && (descriptor->open_mode & O_APPEND) != 0
728			? descriptor->ops->fd_seek(descriptor.Get(), 0, SEEK_END) : pos + length;
729	}
730
731	return length <= SSIZE_MAX ? (ssize_t)length : SSIZE_MAX;
732}
733
734
735static ssize_t
736common_user_vector_io(int fd, off_t pos, const iovec* userVecs, size_t count,
737	bool write)
738{
739	if (pos < -1)
740		return B_BAD_VALUE;
741
742	iovec* vecs;
743	status_t error = get_iovecs_from_user(userVecs, count, vecs, true);
744	if (error != B_OK)
745		return error;
746	MemoryDeleter _(vecs);
747
748	FileDescriptorPutter descriptor(get_fd(get_current_io_context(false), fd));
749	if (!descriptor.IsSet())
750		return B_FILE_ERROR;
751
752	if (write ? (descriptor->open_mode & O_RWMASK) == O_RDONLY
753			: (descriptor->open_mode & O_RWMASK) == O_WRONLY) {
754		return B_FILE_ERROR;
755	}
756
757	bool movePosition = false;
758	if (pos == -1 && descriptor->pos != -1) {
759		pos = descriptor->pos;
760		movePosition = true;
761	}
762
763	if (write ? descriptor->ops->fd_write == NULL
764			: descriptor->ops->fd_read == NULL) {
765		return B_BAD_VALUE;
766	}
767
768	SyscallRestartWrapper<status_t> status;
769
770	ssize_t bytesTransferred = 0;
771	for (size_t i = 0; i < count; i++) {
772		if (vecs[i].iov_base == NULL)
773			continue;
774
775		size_t length = vecs[i].iov_len;
776		if (write) {
777			status = descriptor->ops->fd_write(descriptor.Get(), pos,
778				vecs[i].iov_base, &length);
779		} else {
780			status = descriptor->ops->fd_read(descriptor.Get(), pos, vecs[i].iov_base,
781				&length);
782		}
783
784		if (status != B_OK) {
785			if (bytesTransferred == 0)
786				return status;
787			status = B_OK;
788			break;
789		}
790
791		if ((uint64)bytesTransferred + length > SSIZE_MAX)
792			bytesTransferred = SSIZE_MAX;
793		else
794			bytesTransferred += (ssize_t)length;
795
796		pos += length;
797
798		if (length < vecs[i].iov_len)
799			break;
800	}
801
802	if (movePosition) {
803		descriptor->pos = write && (descriptor->open_mode & O_APPEND) != 0
804			? descriptor->ops->fd_seek(descriptor.Get(), 0, SEEK_END) : pos;
805	}
806
807	return bytesTransferred;
808}
809
810
811status_t
812user_fd_kernel_ioctl(int fd, uint32 op, void* buffer, size_t length)
813{
814	TRACE(("user_fd_kernel_ioctl: fd %d\n", fd));
815
816	return fd_ioctl(false, fd, op, buffer, length);
817}
818
819
820//	#pragma mark - User syscalls
821
822
823ssize_t
824_user_read(int fd, off_t pos, void* buffer, size_t length)
825{
826	return common_user_io(fd, pos, buffer, length, false);
827}
828
829
830ssize_t
831_user_readv(int fd, off_t pos, const iovec* userVecs, size_t count)
832{
833	return common_user_vector_io(fd, pos, userVecs, count, false);
834}
835
836
837ssize_t
838_user_write(int fd, off_t pos, const void* buffer, size_t length)
839{
840	return common_user_io(fd, pos, (void*)buffer, length, true);
841}
842
843
844ssize_t
845_user_writev(int fd, off_t pos, const iovec* userVecs, size_t count)
846{
847	return common_user_vector_io(fd, pos, userVecs, count, true);
848}
849
850
851off_t
852_user_seek(int fd, off_t pos, int seekType)
853{
854	syscall_64_bit_return_value();
855
856	FileDescriptorPutter descriptor(get_fd(get_current_io_context(false), fd));
857	if (!descriptor.IsSet())
858		return B_FILE_ERROR;
859
860	TRACE(("user_seek(descriptor = %p)\n", descriptor));
861
862	if (descriptor->ops->fd_seek)
863		pos = descriptor->ops->fd_seek(descriptor.Get(), pos, seekType);
864	else
865		pos = ESPIPE;
866
867	return pos;
868}
869
870
871status_t
872_user_ioctl(int fd, uint32 op, void* buffer, size_t length)
873{
874	TRACE(("user_ioctl: fd %d\n", fd));
875
876	// "buffer" is not always a pointer depending on "op", so we cannot
877	// check that it is a userland buffer here. Instead we check that
878	// it is at least not within the bounds of kernel memory; as in
879	// the cases where it is a numeric constant it is usually a low one.
880	if (IS_KERNEL_ADDRESS(buffer))
881		return B_BAD_ADDRESS;
882
883	SyscallRestartWrapper<status_t> status;
884
885	return status = fd_ioctl(false, fd, op, buffer, length);
886}
887
888
889ssize_t
890_user_read_dir(int fd, struct dirent* userBuffer, size_t bufferSize,
891	uint32 maxCount)
892{
893	TRACE(("user_read_dir(fd = %d, userBuffer = %p, bufferSize = %ld, count = "
894		"%" B_PRIu32 ")\n", fd, userBuffer, bufferSize, maxCount));
895
896	if (maxCount == 0)
897		return 0;
898
899	if (userBuffer == NULL || !IS_USER_ADDRESS(userBuffer))
900		return B_BAD_ADDRESS;
901
902	// get I/O context and FD
903	io_context* ioContext = get_current_io_context(false);
904	FileDescriptorPutter descriptor(get_fd(ioContext, fd));
905	if (!descriptor.IsSet())
906		return B_FILE_ERROR;
907
908	if (descriptor->ops->fd_read_dir == NULL)
909		return B_UNSUPPORTED;
910
911	// restrict buffer size and allocate a heap buffer
912	if (bufferSize > kMaxReadDirBufferSize)
913		bufferSize = kMaxReadDirBufferSize;
914	struct dirent* buffer = (struct dirent*)malloc(bufferSize);
915	if (buffer == NULL)
916		return B_NO_MEMORY;
917	MemoryDeleter bufferDeleter(buffer);
918
919	// read the directory
920	uint32 count = maxCount;
921	status_t status = descriptor->ops->fd_read_dir(ioContext, descriptor.Get(),
922		buffer, bufferSize, &count);
923	if (status != B_OK)
924		return status;
925
926	ASSERT(count <= maxCount);
927
928	// copy the buffer back -- determine the total buffer size first
929	size_t sizeToCopy = 0;
930	BytePointer<struct dirent> entry = buffer;
931	for (uint32 i = 0; i < count; i++) {
932		size_t length = entry->d_reclen;
933		sizeToCopy += length;
934		entry += length;
935	}
936
937	ASSERT(sizeToCopy <= bufferSize);
938
939	if (user_memcpy(userBuffer, buffer, sizeToCopy) != B_OK)
940		return B_BAD_ADDRESS;
941
942	return count;
943}
944
945
946status_t
947_user_rewind_dir(int fd)
948{
949	TRACE(("user_rewind_dir(fd = %d)\n", fd));
950
951	FileDescriptorPutter descriptor(get_fd(get_current_io_context(false), fd));
952	if (!descriptor.IsSet())
953		return B_FILE_ERROR;
954
955	status_t status;
956	if (descriptor->ops->fd_rewind_dir)
957		status = descriptor->ops->fd_rewind_dir(descriptor.Get());
958	else
959		status = B_UNSUPPORTED;
960
961	return status;
962}
963
964
965status_t
966_user_close(int fd)
967{
968	return common_close(fd, false);
969}
970
971
972int
973_user_dup(int fd)
974{
975	return dup_fd(fd, false);
976}
977
978
979int
980_user_dup2(int ofd, int nfd)
981{
982	return dup2_fd(ofd, nfd, false);
983}
984
985
986//	#pragma mark - Kernel calls
987
988
989ssize_t
990_kern_read(int fd, off_t pos, void* buffer, size_t length)
991{
992	if (pos < -1)
993		return B_BAD_VALUE;
994
995	FileDescriptorPutter descriptor(get_fd(get_current_io_context(true), fd));
996
997	if (!descriptor.IsSet())
998		return B_FILE_ERROR;
999	if ((descriptor->open_mode & O_RWMASK) == O_WRONLY)
1000		return B_FILE_ERROR;
1001
1002	bool movePosition = false;
1003	if (pos == -1 && descriptor->pos != -1) {
1004		pos = descriptor->pos;
1005		movePosition = true;
1006	}
1007
1008	SyscallFlagUnsetter _;
1009
1010	if (descriptor->ops->fd_read == NULL)
1011		return B_BAD_VALUE;
1012
1013	ssize_t bytesRead = descriptor->ops->fd_read(descriptor.Get(), pos, buffer,
1014		&length);
1015	if (bytesRead >= B_OK) {
1016		if (length > SSIZE_MAX)
1017			bytesRead = SSIZE_MAX;
1018		else
1019			bytesRead = (ssize_t)length;
1020
1021		if (movePosition)
1022			descriptor->pos = pos + length;
1023	}
1024
1025	return bytesRead;
1026}
1027
1028
1029ssize_t
1030_kern_readv(int fd, off_t pos, const iovec* vecs, size_t count)
1031{
1032	status_t status;
1033
1034	if (pos < -1)
1035		return B_BAD_VALUE;
1036
1037	FileDescriptorPutter descriptor(get_fd(get_current_io_context(true), fd));
1038
1039	if (!descriptor.IsSet())
1040		return B_FILE_ERROR;
1041	if ((descriptor->open_mode & O_RWMASK) == O_WRONLY)
1042		return B_FILE_ERROR;
1043
1044	bool movePosition = false;
1045	if (pos == -1 && descriptor->pos != -1) {
1046		pos = descriptor->pos;
1047		movePosition = true;
1048	}
1049
1050	if (descriptor->ops->fd_read == NULL)
1051		return B_BAD_VALUE;
1052
1053	SyscallFlagUnsetter _;
1054
1055	ssize_t bytesRead = 0;
1056
1057	for (size_t i = 0; i < count; i++) {
1058		size_t length = vecs[i].iov_len;
1059		status = descriptor->ops->fd_read(descriptor.Get(), pos,
1060			vecs[i].iov_base, &length);
1061		if (status != B_OK) {
1062			bytesRead = status;
1063			break;
1064		}
1065
1066		if ((uint64)bytesRead + length > SSIZE_MAX)
1067			bytesRead = SSIZE_MAX;
1068		else
1069			bytesRead += (ssize_t)length;
1070
1071		pos += vecs[i].iov_len;
1072	}
1073
1074	if (movePosition)
1075		descriptor->pos = pos;
1076
1077	return bytesRead;
1078}
1079
1080
1081ssize_t
1082_kern_write(int fd, off_t pos, const void* buffer, size_t length)
1083{
1084	if (pos < -1)
1085		return B_BAD_VALUE;
1086
1087	FileDescriptorPutter descriptor(get_fd(get_current_io_context(true), fd));
1088
1089	if (!descriptor.IsSet())
1090		return B_FILE_ERROR;
1091	if ((descriptor->open_mode & O_RWMASK) == O_RDONLY)
1092		return B_FILE_ERROR;
1093
1094	bool movePosition = false;
1095	if (pos == -1 && descriptor->pos != -1) {
1096		pos = descriptor->pos;
1097		movePosition = true;
1098	}
1099
1100	if (descriptor->ops->fd_write == NULL)
1101		return B_BAD_VALUE;
1102
1103	SyscallFlagUnsetter _;
1104
1105	ssize_t bytesWritten = descriptor->ops->fd_write(descriptor.Get(), pos,
1106		buffer,	&length);
1107	if (bytesWritten >= B_OK) {
1108		if (length > SSIZE_MAX)
1109			bytesWritten = SSIZE_MAX;
1110		else
1111			bytesWritten = (ssize_t)length;
1112
1113		if (movePosition)
1114			descriptor->pos = pos + length;
1115	}
1116
1117	return bytesWritten;
1118}
1119
1120
1121ssize_t
1122_kern_writev(int fd, off_t pos, const iovec* vecs, size_t count)
1123{
1124	status_t status;
1125
1126	if (pos < -1)
1127		return B_BAD_VALUE;
1128
1129	FileDescriptorPutter descriptor(get_fd(get_current_io_context(true), fd));
1130
1131	if (!descriptor.IsSet())
1132		return B_FILE_ERROR;
1133	if ((descriptor->open_mode & O_RWMASK) == O_RDONLY)
1134		return B_FILE_ERROR;
1135
1136	bool movePosition = false;
1137	if (pos == -1 && descriptor->pos != -1) {
1138		pos = descriptor->pos;
1139		movePosition = true;
1140	}
1141
1142	if (descriptor->ops->fd_write == NULL)
1143		return B_BAD_VALUE;
1144
1145	SyscallFlagUnsetter _;
1146
1147	ssize_t bytesWritten = 0;
1148
1149	for (size_t i = 0; i < count; i++) {
1150		size_t length = vecs[i].iov_len;
1151		status = descriptor->ops->fd_write(descriptor.Get(), pos,
1152			vecs[i].iov_base, &length);
1153		if (status != B_OK) {
1154			bytesWritten = status;
1155			break;
1156		}
1157
1158		if ((uint64)bytesWritten + length > SSIZE_MAX)
1159			bytesWritten = SSIZE_MAX;
1160		else
1161			bytesWritten += (ssize_t)length;
1162
1163		pos += vecs[i].iov_len;
1164	}
1165
1166	if (movePosition)
1167		descriptor->pos = pos;
1168
1169	return bytesWritten;
1170}
1171
1172
1173off_t
1174_kern_seek(int fd, off_t pos, int seekType)
1175{
1176	FileDescriptorPutter descriptor(get_fd(get_current_io_context(true), fd));
1177	if (!descriptor.IsSet())
1178		return B_FILE_ERROR;
1179
1180	if (descriptor->ops->fd_seek)
1181		pos = descriptor->ops->fd_seek(descriptor.Get(), pos, seekType);
1182	else
1183		pos = ESPIPE;
1184
1185	return pos;
1186}
1187
1188
1189status_t
1190_kern_ioctl(int fd, uint32 op, void* buffer, size_t length)
1191{
1192	TRACE(("kern_ioctl: fd %d\n", fd));
1193
1194	SyscallFlagUnsetter _;
1195
1196	return fd_ioctl(true, fd, op, buffer, length);
1197}
1198
1199
1200ssize_t
1201_kern_read_dir(int fd, struct dirent* buffer, size_t bufferSize,
1202	uint32 maxCount)
1203{
1204	TRACE(("sys_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = "
1205		"%" B_PRIu32 ")\n",fd, buffer, bufferSize, maxCount));
1206
1207	struct io_context* ioContext = get_current_io_context(true);
1208	FileDescriptorPutter descriptor(get_fd(ioContext, fd));
1209	if (!descriptor.IsSet())
1210		return B_FILE_ERROR;
1211
1212	ssize_t retval;
1213	if (descriptor->ops->fd_read_dir) {
1214		uint32 count = maxCount;
1215		retval = descriptor->ops->fd_read_dir(ioContext, descriptor.Get(), buffer,
1216			bufferSize, &count);
1217		if (retval >= 0)
1218			retval = count;
1219	} else
1220		retval = B_UNSUPPORTED;
1221
1222	return retval;
1223}
1224
1225
1226status_t
1227_kern_rewind_dir(int fd)
1228{
1229	TRACE(("sys_rewind_dir(fd = %d)\n",fd));
1230
1231	FileDescriptorPutter descriptor(get_fd(get_current_io_context(true), fd));
1232	if (!descriptor.IsSet())
1233		return B_FILE_ERROR;
1234
1235	status_t status;
1236	if (descriptor->ops->fd_rewind_dir)
1237		status = descriptor->ops->fd_rewind_dir(descriptor.Get());
1238	else
1239		status = B_UNSUPPORTED;
1240
1241	return status;
1242}
1243
1244
1245status_t
1246_kern_close(int fd)
1247{
1248	return common_close(fd, true);
1249}
1250
1251
1252int
1253_kern_dup(int fd)
1254{
1255	return dup_fd(fd, true);
1256}
1257
1258
1259int
1260_kern_dup2(int ofd, int nfd)
1261{
1262	return dup2_fd(ofd, nfd, true);
1263}
1264
1265