File: open.c

package info (click to toggle)
ibcs 971113-3
  • links: PTS
  • area: main
  • in suites: hamm
  • size: 1,108 kB
  • ctags: 2,061
  • sloc: ansic: 14,910; makefile: 280; sh: 203; asm: 47; perl: 18; pascal: 2
file content (560 lines) | stat: -rw-r--r-- 14,171 bytes parent folder | download | duplicates (2)
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
/*
 *  linux/ibcs/open.c
 *
 *  Copyright (C) 1993  Joe Portman (baron@hebron.connected.com)
 *  Copyright (C) 1993, 1994  Drew Sullivan (re-worked for iBCS2)
 *
 * $Id: open.c,v 1.36 1997/05/13 19:56:29 jaggy Exp $
 * $Source: /usr/CVS/ibcs/iBCSemul/open.c,v $
 */

/* Keep track of which struct definition we really want */

#include <linux/config.h>

#include <linux/module.h>
#include <linux/version.h>

#include <asm/segment.h>
#ifndef KERNEL_DS
#include <linux/segment.h>
#endif

#include <linux/vfs.h>
#include <linux/types.h>
#include <linux/utime.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/tty.h>
#include <linux/time.h>
#include <linux/malloc.h>
#include <linux/un.h>

#include <asm/bitops.h>

#include <ibcs/ibcs.h>

#ifdef __NR_getdents
#include <linux/dirent.h>
#endif

#ifdef IBCS_TRACE
#include <ibcs/trace.h>
#endif


#ifdef __cplusplus
extern "C" 
#endif

/* ISC (at least) assumes O_CREAT if O_TRUNC is given. This is emulated
 * here but is it correct for iBCS in general? Do we care?
 */
static unsigned short fl_ibcs_to_linux[] = {
	0x0001, 0x0002, 0x0800, 0x0400, 0x1000, 0x0000, 0x0000, 0x0800,
	0x0040, 0x0240, 0x0080, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000
};

static unsigned short fl_linux_to_ibcs[] = {
	0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0400,
	0x0800, 0x0200, 0x0008, 0x0004, 0x0010, 0x0000, 0x0000, 0x0000
};

static inline unsigned short map_flags(unsigned short f, unsigned short map[])
{
	int i;
	unsigned short m, r;

	r = 0;
	for (i=0,m=1; i < 16; i++,m<<=1)
		if (f & m)
			r |= map[i];

	return r;
}


int ibcs_statfs(const char * path, struct ibcs_statfs * buf, int len, int fstype)
{
	struct inode * inode;
	struct statfs lxstat;
	struct ibcs_statfs ibcsstat;
	int error;
	int old_fs;

	if (len > (int)sizeof(struct ibcs_statfs))
		return -EINVAL;

	error = verify_area(VERIFY_WRITE, buf, len);
	if (error)
		return error;

	if (!fstype) {
		error = namei(path,&inode);
		if (error)
			return error;
		if (!inode->i_sb->s_op->statfs) {
			iput(inode);
			return -ENOSYS;
		}
		old_fs = get_fs();
		set_fs(get_ds());

#ifdef __NR_getdents
		inode->i_sb->s_op->statfs(inode->i_sb, &lxstat, sizeof(lxstat));
#else
		inode->i_sb->s_op->statfs(inode->i_sb, &lxstat);
#endif

		set_fs(old_fs);
		iput(inode);

		ibcsstat.f_type = lxstat.f_type;
		ibcsstat.f_bsize = lxstat.f_bsize;
		ibcsstat.f_frsize = 0;
		ibcsstat.f_blocks = lxstat.f_blocks;
		ibcsstat.f_bfree = lxstat.f_bfree;
		ibcsstat.f_files = lxstat.f_files;
		ibcsstat.f_ffree = lxstat.f_ffree;
		memset(ibcsstat.f_fname, 0, sizeof(ibcsstat.f_fname));
		memset(ibcsstat.f_fpack, 0, sizeof(ibcsstat.f_fpack));

		/* Finally, copy it to the user's buffer */
		memcpy_tofs(buf, &ibcsstat, len);
		return 0;
	}

	/* Linux can't stat unmounted filesystems so we
	 * simply lie and claim 100MB of 1GB is free. Sorry.
	 */
	ibcsstat.f_bsize = 1024;
	ibcsstat.f_frsize = 0;
	ibcsstat.f_blocks = 1024 * 1024;	/* 1GB */
	ibcsstat.f_bfree = 100 * 1024;		/* 100MB */
	ibcsstat.f_files = 60000;
	ibcsstat.f_ffree = 50000;
	memset(ibcsstat.f_fname, 0, sizeof(ibcsstat.f_fname));
	memset(ibcsstat.f_fpack, 0, sizeof(ibcsstat.f_fpack));

	/* Finally, copy it to the user's buffer */
	memcpy_tofs(buf, &ibcsstat, len);
	return 0;
}

#ifdef __cplusplus
extern "C" 
#endif
int ibcs_fstatfs(unsigned int fd, struct ibcs_statfs * buf, int len, int fstype)
{
	struct inode * inode;
	struct file * file;
	struct statfs lxstat;
	struct ibcs_statfs ibcsstat;
	int error;
	int old_fs;

	if (len > (int)sizeof(struct ibcs_statfs))
		return -EINVAL;

	error = verify_area(VERIFY_WRITE, buf, len);
	if (error)
		return error;

	if (!fstype) {
		if (fd >= NR_OPEN || !(file = current->FD[fd]))
			return -EBADF;
		if (!(inode = file->f_inode))
			return -ENOENT;
		if (!inode->i_sb->s_op->statfs)
			return -ENOSYS;

		old_fs = get_fs();
		set_fs(get_ds());

#ifdef __NR_getdents
		inode->i_sb->s_op->statfs(inode->i_sb, &lxstat, sizeof(lxstat));
#else
		inode->i_sb->s_op->statfs(inode->i_sb, &lxstat);
#endif

		set_fs(old_fs);

		ibcsstat.f_type = lxstat.f_type;
		ibcsstat.f_bsize = lxstat.f_bsize;
		ibcsstat.f_frsize = 0;
		ibcsstat.f_blocks = lxstat.f_blocks;
		ibcsstat.f_bfree = lxstat.f_bfree;
		ibcsstat.f_files = lxstat.f_files;
		ibcsstat.f_ffree = lxstat.f_ffree;
		memset(ibcsstat.f_fname, 0, sizeof(ibcsstat.f_fname));
		memset(ibcsstat.f_fpack, 0, sizeof(ibcsstat.f_fpack));

		/* Finally, copy it to the user's buffer */
		memcpy_tofs(buf, &ibcsstat, len);
		return 0;
	}

	/* Linux can't stat unmounted filesystems so we
	 * simply lie and claim 100MB is of 1GB free. Sorry.
	 */
	ibcsstat.f_bsize = 1024;
	ibcsstat.f_frsize = 0;
	ibcsstat.f_blocks = 1024 * 1024;	/* 1GB */
	ibcsstat.f_bfree = 100 * 1024;		/* 100MB */
	ibcsstat.f_files = 60000;
	ibcsstat.f_ffree = 50000;
	memset(ibcsstat.f_fname, 0, sizeof(ibcsstat.f_fname));
	memset(ibcsstat.f_fpack, 0, sizeof(ibcsstat.f_fpack));

	/* Finally, copy it to the user's buffer */
	memcpy_tofs(buf, &ibcsstat, len);
	return 0;
}


int ibcs_mkdir(const char *fname, int mode)
{
	int error, old_fs;
	char *tmp, *p;

	if ((error = getname(fname, &tmp)))
		return error;

	/* Drop any trailing slash */
	for (p=tmp; *p; p++);
	p--;
	if (*p == '/')
		*p = '\0';

	old_fs = get_fs();
	set_fs(get_ds());
	error = SYS(mkdir)(tmp, mode);
	set_fs(old_fs);

	putname(tmp);
	return error;
}


int ibcs_mknod(const char *fname, int mode, int dev)
{
	/* Linux doesn't allow us to create a directory with mknod(). */
	if ((mode & 0017000) == 0040000)
		return ibcs_mkdir(fname, mode);
	return SYS(mknod)(fname, mode, dev);
}


int ibcs_open(const char *fname, int flag, int mode)
{
#ifdef __sparc__
	return SYS(open)(fname, map_flags(flag, fl_ibcs_to_linux), mode);
#else
	int error, fd, old_fs, args[3];
	char *p;
	struct sockaddr_un addr;

	fd = SYS(open)(fname, map_flags(flag, fl_ibcs_to_linux), mode);
	if (fd < 0)
		return fd;

	/* Sometimes a program may open a pathname which it expects
	 * to be a named pipe (or STREAMS named pipe) when the
	 * Linux domain equivalent is a Unix domain socket. (e.g.
	 * UnixWare uses a STREAMS named pipe /dev/X/Nserver.0 for
	 * X :0 but Linux uses a Unix domain socket /tmp/.X11-unix/X0)
	 * It isn't enough just to make the symlink because you cannot
	 * open() a socket and read/write it. If we spot the error we can
	 * switch to socket(), connect() and things will likely work
	 * as expected however.
	 */
	if (!S_ISSOCK(current->FD[fd]->f_inode->i_mode))
		return fd;

	SYS(close)(fd);
	args[0] = AF_UNIX;
	args[1] = SOCK_STREAM;
	args[2] = 0;
	old_fs = get_fs();
	set_fs(get_ds());
	fd = SYS(socketcall)(SYS_SOCKET, args);
	set_fs(old_fs);
	if (fd < 0)
		return fd;

	if ((error = getname(fname, &p))) {
		SYS(close)(fd);
		return error;
	}
	if (strlen(p) >= UNIX_PATH_MAX) {
		putname(p);
		SYS(close)(fd);
		return -E2BIG;
	}
	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path, p);
	putname(p);

	args[0] = fd;
	args[1] = (int)&addr;
	args[2] = sizeof(struct sockaddr_un);
	set_fs(get_ds());
	error = SYS(socketcall)(SYS_CONNECT, args);
	set_fs(old_fs);
	if (error) {
		SYS(close)(fd);
		return error;
	}

	return fd;
#endif
}


/* If/when the readdir function is changed to read multiple entries
 * at once this should be updated to take advantage of the fact.
 *
 * N.B. For Linux the reclen in a dirent is the number of characters
 * in the filename, for SCO (at least) reclen is the total size of
 * the particular dirent rounded up to the next multiple of 4. The SCO
 * behaviour is faithfully emulated here.
 *
 * XXXX
 * We don't truncate long filenames at all when copying. If we meet a
 * long filename and the buffer supplied by the application simply isn't
 * big enough to hold it we'll return without filling the buffer (i.e
 * return 0). The application will see this as a (premature) end of
 * directory. Is there a work around for this at all???
 */
int ibcs_getdents(int fd, char *buf, int nbytes)
{
	int error, here, posn, reclen;
	struct file *file;
	struct dirent *d;
	int old_fs;

	error = verify_area(VERIFY_WRITE, buf, nbytes);
	if (error)
		return error;

	/* Check the file handle here. This is so we can access the current
	 * position in the file structure safely without a tedious call
	 * to sys_lseek that does nothing useful.
	 */
	if (fd < 0 || fd >= NR_OPEN
	|| !(file=current->FD[fd])
	|| !(file->f_inode))
		return -EBADF;

	d = (struct dirent *)__get_free_page(GFP_KERNEL);
	if (!d)
		return -ENOMEM;

	error = posn = reclen = 0;
	while (posn + reclen < nbytes) {
		/* Save the current position and get another dirent */
		here = file->f_pos;
		old_fs = get_fs();
		set_fs (get_ds());
		error = SYS(readdir)(fd, d, 1);
		set_fs(old_fs);
		if (error <= 0)
			break;

		/* If it'll fit in the buffer save it otherwise back up
		 * so it is read next time around.
		 * Oh, if we're at the beginning of the buffer there's
		 * no chance that this entry will ever fit so don't
		 * copy it and don't back off - we'll just pretend it
		 * isn't here...
		 */
		reclen = (sizeof(long) + sizeof(off_t)
			+ sizeof(unsigned short) + d->d_reclen + 1
			+ 3) & (~3);
		if (posn + reclen <= nbytes) {
			if (current->personality & PER_SHORT_INODE) {
				/* read() on a directory only handles
				 * short inodes but cannot use 0 as that
				 * indicates an empty directory slot.
				 * Therefore stat() must also fold
				 * inode numbers avoiding 0. Which in
				 * turn means that getdents() must fold
				 * inodes avoiding 0 - if the program
				 * was built in a short inode environment.
				 * If we have short inodes in the dirent
				 * we also have a two byte pad so we
				 * can let the high word fall in the pad.
				 * This makes it a little more robust if
				 * we guessed the inode size wrong.
				 */
				if (!((unsigned long)d->d_ino & 0xffff))
					d->d_ino = 0xfffffffe;
			}
			d->d_reclen = reclen;
			d->d_off = file->f_pos;
			memcpy_tofs(buf+posn, d, reclen);
			posn += reclen;
		} else if (posn) {
			SYS(lseek)(fd, here, 0);
		} /* else posn == 0 */
	}

	/* Loose the intermediate buffer. */
	free_page((unsigned long)d);

	/* If we've put something in the buffer return the byte count
	 * otherwise return the error status.
	 */
	return ((posn > 0) ? posn : error);
}


struct ibcs_flock {
	short l_type;	/* numbers don't match */
	short l_whence;
	off_t l_start;
	off_t l_len;	/* 0 means to end of file */
	short l_sysid;
	short l_pid;
};


int
ibcs_fcntl(struct pt_regs *regs)
{
	int arg1, arg2, arg3;
	int error, retval;

#ifndef __sparc__
	error = verify_area(VERIFY_READ,
			((unsigned long *)regs->esp)+1,
			3*sizeof(long));
	if (error)
		return error;
#endif /* __sparc__ */
	arg1 = get_syscall_parameter (regs, 0);
	arg2 = get_syscall_parameter (regs, 1);
	arg3 = get_syscall_parameter (regs, 2);

	switch (arg2) {
		/* These match the Linux commands. */
		case 0: /* F_DUPFD */
		case 1: /* F_GETFD */
		case 2: /* F_SETFD */
			return SYS(fcntl)(arg1, arg2, arg3);

		/* The iBCS flags don't match Linux flags. */
		case 3: /* F_GETFL */
			return map_flags(SYS(fcntl)(arg1, arg2, arg3),
					fl_linux_to_ibcs);
		case 4: /* F_SETFL */
			arg3 = map_flags(arg3, fl_ibcs_to_linux);
			return SYS(fcntl)(arg1, arg2, arg3);

		/* The lock stucture is different. */
		case 14: /* F_GETLK SVR4 */
			arg2 = 5;
			/* fall through */
		case 5: /* F_GETLK */
		case 6: /* F_SETLK */
		case 7: /* F_SETLKW */
#ifdef __sparc__
		        (((struct ibcs_flock *)arg3)->l_type)--;
#else /* __sparc__ */
			__asm__("decw %%fs:%0"
				: /* no outputs */
				: "m" (((struct ibcs_flock *)arg3)->l_type));
#endif /* __sparc__ */
#ifdef IBCS_TRACE
			if ((ibcs_trace & TRACE_API) || ibcs_func_p->trace) {
				struct ibcs_flock fl;

  				memcpy_fromfs(&fl,(void *)arg3,sizeof(fl));
				printk (KERN_DEBUG "iBCS: lock l_type: %d l_whence: %d l_start: %lu l_len: %lu l_sysid: %d l_pid: %d\n",
							fl.l_type,
							fl.l_whence,
							fl.l_start,
							fl.l_len,
							fl.l_sysid,
							fl.l_pid);

				}
#endif
			/* okay do some magic here */
			retval = SYS(fcntl)(arg1, arg2, arg3);
#ifdef __sparc__
			(((struct ibcs_flock *)arg3)->l_type)++;
#else /* __sparc__ */
			__asm__("incw %%fs:%0"
				: /* no outputs */
				: "m" (((struct ibcs_flock *)arg3)->l_type));
#endif /* __sparc__ */
			/* Linux doesn't have the sysid field so we need to
			 * roll the pid down a slot.
			 */
			put_fs_word(
				get_fs_word(&(((struct ibcs_flock *)arg3)->l_sysid)),
				&(((struct ibcs_flock *)arg3)->l_pid));
			put_fs_word(0,
				&(((struct ibcs_flock *)arg3)->l_sysid));

			return retval;

		case 10: /* F_ALLOCSP */
			/* Extend allocation for specified portion of file. */
		case 11: /* F_FREESP */
			/* Free a portion of a file. */
			return 0;

		/* These are intended to support the Xenix chsize() and
		 * rdchk() system calls. I don't know if these may be
		 * generated by applications or not.
		 */
		case 0x6000: /* F_CHSIZE */
			return SYS(ftruncate)(arg1, arg3);
#ifndef __sparc__
		case 0x6001: /* F_RDCHK */
			return xnx_rdchk(arg1);
#endif /* __sparc__ */

#ifdef EMU_SCO
		/* This could be SCO's get highest fd open if the fd we
		 * are called on is -1 otherwise it could be F_CHKFL.
		 */
		case  8: /* F_GETHFDO */
			if (arg1 == -1)
				return find_first_zero_bit(&current->files->open_fds,
							NR_OPEN);
			/* else fall through to fail */
#else
		/* The following are defined but reserved and unknown. */
		case  8: /* F_CHKFL */
#endif

		/* These are made from the Xenix locking() system call.
		 * According to available documentation these would
		 * never be generated by an application - only by the
		 * kernel Xenix support.
		 */
		case 0x6300: /* F_LK_UNLCK */
		case 0x7200: /* F_LK_LOCK */
		case 0x6200: /* F_LK_NBLCK */
		case 0x7100: /* F_LK_RLCK */
		case 0x6100: /* F_LK_NBRLCK */

		default:
#ifdef IBCS_TRACE
			if ((ibcs_trace & TRACE_API) || ibcs_func_p->trace) {
					printk(KERN_ERR "iBCS: unsupported fcntl 0x%lx, arg 0x%lx\n",
					(unsigned long)arg2, (unsigned long)arg3);
			}
#endif
			return -EINVAL;
			break;
	}
}