source: rtems-libbsd/rtemsbsd/sys/fs/devfs/devfs_devs.c @ 8993827

5-freebsd-12
Last change on this file since 8993827 was 8993827, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 27, 2018 at 12:40:19 PM

DEVFS(5): Properly initialize fp->f_cdevpriv

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <machine/rtems-bsd-kernel-space.h>
33
34#include <sys/types.h>
35#include <sys/conf.h>
36#include <sys/kernel.h>
37#include <sys/file.h>
38#include <sys/malloc.h>
39#include <sys/proc.h>
40#include <sys/poll.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include <fs/devfs/devfs_int.h>
46
47#include <machine/pcpu.h>
48
49#include <rtems/imfs.h>
50
51#define DEVFS_ROOTINO 2
52
53const char rtems_cdev_directory[] = RTEMS_CDEV_DIRECTORY;
54
55/*
56 * The one true (but secret) list of active devices in the system.
57 * Locked by dev_lock()/devmtx
58 */
59struct cdev_priv_list cdevp_list = TAILQ_HEAD_INITIALIZER(cdevp_list);
60
61struct unrhdr *devfs_inos;
62
63static MALLOC_DEFINE(M_CDEVP, "DEVFS1", "DEVFS cdev_priv storage");
64
65static struct cdev *
66devfs_imfs_get_context_by_iop(rtems_libio_t *iop)
67{
68        return IMFS_generic_get_context_by_iop(iop);
69}
70
71static int
72devfs_imfs_open(rtems_libio_t *iop, const char *path, int oflag, mode_t mode)
73{
74        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
75        struct file *fp = rtems_bsd_iop_to_fp(iop);
76        struct thread *td = rtems_bsd_get_curthread_or_null();
77        struct file *fpop;
78        struct cdevsw *dsw;
79        int error, ref;
80
81        if (td != NULL) {
82                if (cdev == NULL) {
83                        error = ENXIO;
84                        goto err;
85                }
86                if (cdev->si_flags & SI_ALIAS) {
87                        cdev = cdev->si_parent;
88                }
89                dsw = dev_refthread(cdev, &ref);
90                if (dsw == NULL) {
91                        error = ENXIO;
92                        goto err;
93                }
94                if (fp == NULL) {
95                        dev_relthread(cdev, ref);
96                        error = ENXIO;
97                        goto err;
98                }
99                fpop = td->td_fpop;
100                curthread->td_fpop = fp;
101                fp->f_cdevpriv = NULL;
102                error = dsw->d_open(cdev, oflag + 1, 0, td);
103                /* Clean up any cdevpriv upon error. */
104                if (error != 0)
105                        devfs_clear_cdevpriv();
106                curthread->td_fpop = fpop;
107                dev_relthread(cdev, ref);
108        } else {
109                error = ENOMEM;
110        }
111
112err:
113        return rtems_bsd_error_to_status_and_errno(error);
114}
115
116static int
117devfs_imfs_close(rtems_libio_t *iop)
118{
119        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
120        struct file *fp = rtems_bsd_iop_to_fp(iop);
121        struct thread *td = rtems_bsd_get_curthread_or_null();
122        int flags = rtems_libio_to_fcntl_flags(iop->flags);
123        struct file *fpop;
124        struct cdevsw *dsw;
125        int error, ref;
126
127        if (td != NULL) {
128                if (cdev == NULL) {
129                        error = ENXIO;
130                        goto err;
131                }
132                if (cdev->si_flags & SI_ALIAS) {
133                        cdev = cdev->si_parent;
134                }
135                dsw = dev_refthread(cdev, &ref);
136                if (dsw == NULL) {
137                        error = ENXIO;
138                        goto err;
139                }
140                fpop = td->td_fpop;
141                curthread->td_fpop = fp;
142                error = dsw->d_close(cdev, flags, 0, td);
143                curthread->td_fpop = fpop;
144                dev_relthread(cdev, ref);
145                if (fp->f_cdevpriv != NULL)
146                        devfs_fpdrop(fp);
147        } else {
148                error = ENOMEM;
149        }
150
151err:
152        return rtems_bsd_error_to_status_and_errno(error);
153}
154
155static ssize_t
156devfs_imfs_readv(rtems_libio_t *iop, const struct iovec *iov, int iovcnt,
157    ssize_t total)
158{
159        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
160        struct file *fp = rtems_bsd_iop_to_fp(iop);
161        struct thread *td = rtems_bsd_get_curthread_or_null();
162        struct uio uio = {
163                .uio_iov = __DECONST(struct iovec *, iov),
164                .uio_iovcnt = iovcnt,
165                .uio_offset = 0,
166                .uio_resid = total,
167                .uio_segflg = UIO_USERSPACE,
168                .uio_rw = UIO_READ,
169                .uio_td = td
170        };
171        struct file *fpop;
172        struct cdevsw *dsw;
173        int error, ref;
174
175        if (td != NULL) {
176                if (cdev == NULL) {
177                        error = ENXIO;
178                        goto err;
179                }
180                if (cdev->si_flags & SI_ALIAS) {
181                        cdev = cdev->si_parent;
182                }
183                dsw = dev_refthread(cdev, &ref);
184                if (dsw == NULL) {
185                        error = ENXIO;
186                        goto err;
187                }
188                fpop = td->td_fpop;
189                curthread->td_fpop = fp;
190                error = dsw->d_read(cdev, &uio,
191                    rtems_libio_to_fcntl_flags(iop->flags));
192                td->td_fpop = fpop;
193                dev_relthread(cdev, ref);
194        } else {
195                error = ENOMEM;
196        }
197
198err:
199        if (error == 0) {
200                return (total - uio.uio_resid);
201        } else {
202                rtems_set_errno_and_return_minus_one(error);
203        }
204}
205
206static ssize_t
207devfs_imfs_read(rtems_libio_t *iop, void *buffer, size_t count)
208{
209        struct iovec iov = {
210                .iov_base = buffer,
211                .iov_len = count
212        };
213
214        return (devfs_imfs_readv(iop, &iov, 1, count));
215}
216
217static ssize_t
218devfs_imfs_writev(rtems_libio_t *iop, const struct iovec *iov, int iovcnt,
219    ssize_t total)
220{
221        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
222        struct file *fp = rtems_bsd_iop_to_fp(iop);
223        struct thread *td = rtems_bsd_get_curthread_or_null();
224        struct uio uio = {
225                .uio_iov = __DECONST(struct iovec *, iov),
226                .uio_iovcnt = iovcnt,
227                .uio_offset = 0,
228                .uio_resid = total,
229                .uio_segflg = UIO_USERSPACE,
230                .uio_rw = UIO_WRITE,
231                .uio_td = td
232        };
233        struct file *fpop;
234        struct cdevsw *dsw;
235        int error, ref;
236
237        if (td != NULL) {
238                if (cdev == NULL) {
239                        error = ENXIO;
240                        goto err;
241                }
242                if (cdev->si_flags & SI_ALIAS) {
243                        cdev = cdev->si_parent;
244                }
245                dsw = dev_refthread(cdev, &ref);
246                if (dsw == NULL) {
247                        error = ENXIO;
248                        goto err;
249                }
250                fpop = td->td_fpop;
251                curthread->td_fpop = fp;
252                error = dsw->d_write(cdev, &uio,
253                    rtems_libio_to_fcntl_flags(iop->flags));
254                td->td_fpop = fpop;
255                dev_relthread(cdev, ref);
256        } else {
257                error = ENOMEM;
258        }
259
260err:
261        if (error == 0) {
262                return (total - uio.uio_resid);
263        } else {
264                rtems_set_errno_and_return_minus_one(error);
265        }
266}
267
268static ssize_t
269devfs_imfs_write(rtems_libio_t *iop, const void *buffer, size_t count)
270{
271        struct iovec iov = {
272                .iov_base = __DECONST(void *, buffer),
273                .iov_len = count
274        };
275
276        return (devfs_imfs_writev(iop, &iov, 1, count));
277}
278
279static int
280devfs_imfs_ioctl(rtems_libio_t *iop, ioctl_command_t request, void *buffer)
281{
282        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
283        struct file *fp = rtems_bsd_iop_to_fp(iop);
284        struct thread *td = rtems_bsd_get_curthread_or_null();
285        struct file *fpop;
286        struct cdevsw *dsw;
287        int error, ref;
288        int flags = rtems_libio_to_fcntl_flags(iop->flags);
289
290        if (td != 0) {
291                if (cdev == NULL) {
292                        error = ENXIO;
293                        goto err;
294                }
295                if (cdev->si_flags & SI_ALIAS) {
296                        cdev = cdev->si_parent;
297                }
298                dsw = dev_refthread(cdev, &ref);
299                if (dsw == NULL) {
300                        error = ENXIO;
301                        goto err;
302                }
303                fpop = td->td_fpop;
304                curthread->td_fpop = fp;
305                error = dsw->d_ioctl(cdev, request, buffer, flags,
306                    td);
307                td->td_fpop = fpop;
308                dev_relthread(cdev, ref);
309        } else {
310                error = ENOMEM;
311        }
312
313err:
314        return rtems_bsd_error_to_status_and_errno(error);
315}
316
317static int
318devfs_imfs_fstat(const rtems_filesystem_location_info_t *loc, struct stat *buf)
319{
320        int rv = 0;
321        const IMFS_jnode_t *the_dev = loc->node_access;
322
323        if (the_dev != NULL) {
324                buf->st_mode = the_dev->st_mode;
325        } else {
326                rv = rtems_filesystem_default_fstat(loc, buf);
327        }
328
329        return rv;
330}
331
332static int
333devfs_imfs_poll(rtems_libio_t *iop, int events)
334{
335        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
336        struct file *fp = rtems_bsd_iop_to_fp(iop);
337        struct thread *td = rtems_bsd_get_curthread_or_wait_forever();
338        struct file *fpop;
339        struct cdevsw *dsw;
340        int error, ref;
341
342        if (cdev == NULL) {
343                return POLLERR;
344        }
345        if (cdev->si_flags & SI_ALIAS) {
346                cdev = cdev->si_parent;
347        }
348        dsw = dev_refthread(cdev, &ref);
349        if (dsw == NULL) {
350                return POLLERR;
351        }
352        fpop = td->td_fpop;
353        curthread->td_fpop = fp;
354        error = dsw->d_poll(cdev, events, td);
355        td->td_fpop = fpop;
356        dev_relthread(cdev, ref);
357
358        return error;
359}
360
361static int
362devfs_imfs_kqfilter(rtems_libio_t *iop, struct knote *kn)
363{
364        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
365        struct file *fp = rtems_bsd_iop_to_fp(iop);
366        struct thread *td = rtems_bsd_get_curthread_or_wait_forever();
367        struct file *fpop;
368        struct cdevsw *dsw;
369        int error, ref;
370
371        if (cdev == NULL) {
372                return EINVAL;
373        }
374        if (cdev->si_flags & SI_ALIAS) {
375                cdev = cdev->si_parent;
376        }
377        dsw = dev_refthread(cdev, &ref);
378        if (dsw == NULL) {
379                return EINVAL;
380        }
381        fpop = td->td_fpop;
382        curthread->td_fpop = fp;
383        error = dsw->d_kqfilter(cdev, kn);
384        td->td_fpop = fpop;
385        dev_relthread(cdev, ref);
386
387        return error;
388}
389
390static const rtems_filesystem_file_handlers_r devfs_imfs_handlers = {
391        .open_h = devfs_imfs_open,
392        .close_h = devfs_imfs_close,
393        .read_h = devfs_imfs_read,
394        .write_h = devfs_imfs_write,
395        .ioctl_h = devfs_imfs_ioctl,
396        .lseek_h = rtems_filesystem_default_lseek_file,
397        .fstat_h = devfs_imfs_fstat,
398        .ftruncate_h = rtems_filesystem_default_ftruncate,
399        .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
400        .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
401        .fcntl_h = rtems_filesystem_default_fcntl,
402        .poll_h = devfs_imfs_poll,
403        .kqfilter_h = devfs_imfs_kqfilter,
404        .readv_h = devfs_imfs_readv,
405        .writev_h = devfs_imfs_writev,
406};
407
408static const IMFS_node_control devfs_imfs_control = IMFS_GENERIC_INITIALIZER(
409    &devfs_imfs_handlers, IMFS_node_initialize_generic,
410    IMFS_node_destroy_default);
411
412struct cdev *
413devfs_alloc(int flags)
414{
415        struct cdev_priv *cdp;
416        struct cdev *cdev;
417
418        cdp = malloc(sizeof *cdp, M_CDEVP, M_ZERO |
419            ((flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK));
420        if (cdp == NULL)
421                return (NULL);
422
423        cdev = &cdp->cdp_c;
424        LIST_INIT(&cdev->si_children);
425
426        memcpy(cdev->si_path, rtems_cdev_directory, sizeof(cdev->si_path));
427        return (cdev);
428}
429
430void
431devfs_free(struct cdev *cdev)
432{
433        struct cdev_priv *cdp;
434
435        cdp = cdev2priv(cdev);
436        devfs_free_cdp_inode(cdp->cdp_inode);
437        free(cdp, M_CDEVP);
438}
439
440/*
441 * Create the directory for a device.
442 * Note: This don't uses dirname() because this function is not defined thread
443 * save in POSIX.
444 */
445static void
446devfs_create_directory(const char *devname)
447{
448        char *dir = NULL;
449        char *lastsep = NULL;
450        int rv;
451        mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
452
453        dir = strdup(devname, M_TEMP);
454        BSD_ASSERT(dir != NULL);
455
456        lastsep = strrchr(dir, '/');
457        if(lastsep != NULL) {
458                *lastsep = 0;
459                rv = rtems_mkdir(dir, mode);
460                BSD_ASSERT(rv == 0);
461        }
462
463        free(dir, M_TEMP);
464}
465
466/*
467 * devfs_create() and devfs_destroy() are called from kern_conf.c and
468 * in both cases the devlock() mutex is held, so no further locking
469 * is necesary and no sleeping allowed.
470 */
471
472void
473devfs_create(struct cdev *dev)
474{
475        struct cdev_priv *cdp;
476        int rv;
477        mode_t mode = S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO;
478
479        devfs_create_directory(dev->si_path);
480
481        rv = IMFS_make_generic_node(dev->si_path, mode, &devfs_imfs_control,
482            dev);
483        BSD_ASSERT(rv == 0);
484
485        cdp = cdev2priv(dev);
486        cdp->cdp_flags |= CDP_ACTIVE;
487        cdp->cdp_inode = alloc_unrl(devfs_inos);
488        dev_refl(dev);
489        TAILQ_INSERT_TAIL(&cdevp_list, cdp, cdp_list);
490}
491
492void
493devfs_destroy(struct cdev *dev)
494{
495        struct cdev_priv *cdp;
496        int rv;
497
498        cdp = cdev2priv(dev);
499        cdp->cdp_flags &= ~CDP_ACTIVE;
500
501        rv = unlink(dev->si_path);
502        BSD_ASSERT(rv == 0);
503
504        /* FIXME: Check if directory is empty and remove it. */
505}
506
507int
508devfs_dev_exists(const char *name)
509{
510        const size_t dirlen = sizeof(rtems_cdev_directory) - 1;
511        const size_t maxnamelen = SPECNAMELEN;
512
513        int rv;
514        char fullpath[dirlen + maxnamelen + 1];
515
516        memcpy(fullpath, rtems_cdev_directory, dirlen);
517        strncpy(fullpath + dirlen, name, maxnamelen);
518
519        rv = access(fullpath, F_OK);
520
521        if(rv == 0)
522                return 1;
523        else
524                return 0;
525}
526
527ino_t
528devfs_alloc_cdp_inode(void)
529{
530
531        return (alloc_unr(devfs_inos));
532}
533
534void
535devfs_free_cdp_inode(ino_t ino)
536{
537
538        if (ino > 0)
539                free_unr(devfs_inos, ino);
540}
541
542static void
543        devfs_devs_init(void *junk __unused)
544{
545
546        devfs_inos = new_unrhdr(DEVFS_ROOTINO + 1, INT_MAX, &devmtx);
547}
548
549SYSINIT(devfs_devs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_devs_init, NULL);
Note: See TracBrowser for help on using the repository browser.