summaryrefslogtreecommitdiff
path: root/minix/lib/libsffs/read.c
blob: bb922f9a12f0333bc6c0f1df046df17032d34ff8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* This file contains file and directory reading file system call handlers.
 *
 * The entry points into this file are:
 *   do_read		perform the READ file system call
 *   do_getdents	perform the GETDENTS file system call
 *
 * Created:
 *   April 2009 (D.C. van Moolenbroek)
 */

#include "inc.h"

#include <dirent.h>

/*===========================================================================*
 *				do_read					     *
 *===========================================================================*/
ssize_t do_read(ino_t ino_nr, struct fsdriver_data *data, size_t count,
	off_t pos, int call)
{
/* Read data from a file.
 */
  struct inode *ino;
  size_t size, off;
  char *ptr;
  int r, chunk;

  if ((ino = find_inode(ino_nr)) == NULL)
	return EINVAL;

  if (IS_DIR(ino)) return EISDIR;

  if ((r = get_handle(ino)) != OK)
	return r;

  assert(count > 0);

  /* Use the buffer from below to eliminate extra copying. */
  size = sffs_table->t_readbuf(&ptr);
  off = 0;

  while (count > 0) {
	chunk = MIN(count, size);

	if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
		break;

	chunk = r;

	if ((r = fsdriver_copyout(data, off, ptr, chunk)) != OK)
		break;

	count -= chunk;
	off += chunk;
	pos += chunk;
  }

  if (r < 0)
	return r;

  return off;
}

/*===========================================================================*
 *				do_getdents				     *
 *===========================================================================*/
ssize_t do_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
	off_t *posp)
{
/* Retrieve directory entries.
 */
  struct fsdriver_dentry fsdentry;
  char name[NAME_MAX+1];
  struct inode *ino, *child;
  struct sffs_attr attr;
  off_t pos;
  int r;
  /* must be at least sizeof(struct dirent) + NAME_MAX */
  static char buf[BLOCK_SIZE];

  if ((ino = find_inode(ino_nr)) == NULL)
	return EINVAL;

  if (!IS_DIR(ino)) return ENOTDIR;

  if (*posp < 0 || *posp >= ULONG_MAX) return EINVAL;

  /* We are going to need at least one free inode to store children in. */
  if (!have_free_inode()) return ENFILE;

  /* If we don't have a directory handle yet, get one now. */
  if ((r = get_handle(ino)) != OK)
	return r;

  fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf));

  /* We use the seek position as file index number. The first position is for
   * the "." entry, the second position is for the ".." entry, and the next
   * position numbers each represent a file in the directory.
   */
  for (;;) {
	/* Determine which inode and name to use for this entry.
	 * We have no idea whether the host will give us "." and/or "..",
	 * so generate our own and skip those from the host.
	 */
	pos = (*posp)++;

	if (pos == 0) {
		/* Entry for ".". */
		child = ino;

		strcpy(name, ".");

		get_inode(child);
	}
	else if (pos == 1) {
		/* Entry for "..", but only when there is a parent. */
		if (ino->i_parent == NULL)
			continue;

		child = ino->i_parent;

		strcpy(name, "..");

		get_inode(child);
	}
	else {
		/* Any other entry, not being "." or "..". */
		attr.a_mask = SFFS_ATTR_MODE;

		r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
			sizeof(name), &attr);

		if (r != OK) {
			/* No more entries? Then close the handle and stop. */
			if (r == ENOENT) {
				put_handle(ino);

				break;
			}

			/* FIXME: what if the error is ENAMETOOLONG? */
			return r;
		}

		if (!strcmp(name, ".") || !strcmp(name, ".."))
			continue;

		if ((child = lookup_dentry(ino, name)) == NULL) {
			child = get_free_inode();

			/* We were promised a free inode! */
			assert(child != NULL);

			child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);

			add_dentry(ino, name, child);
		}
	}

	r = fsdriver_dentry_add(&fsdentry, INODE_NR(child), name, strlen(name),
		IS_DIR(child) ? DT_DIR : DT_REG);

	put_inode(child);

	if (r < 0)
		return r;
	if (r == 0)
		break;
  }

  return fsdriver_dentry_finish(&fsdentry);
}