[build] legacy files update
[platform/upstream/multipath-tools.git] / libcheckers / directio.c
1 /*
2  * Copyright (c) 2005 Hannes Reinecke, Suse
3  */
4 #define _GNU_SOURCE
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13 #include <linux/fs.h>
14 #include <errno.h>
15
16 #include "path_state.h"
17 #include "checkers.h"
18
19 #define MSG_DIRECTIO_UNKNOWN    "directio checker is not available"
20 #define MSG_DIRECTIO_UP         "directio checker reports path is up"
21 #define MSG_DIRECTIO_DOWN       "directio checker reports path is down"
22
23 struct readsector0_checker_context {
24         void * dummy;
25 };
26
27 static int
28 direct_read (int fd, unsigned char * buff, int size)
29 {
30         long flags;
31         int reset_flags = 0;
32         int res, retval;
33
34         flags = fcntl(fd,F_GETFL);
35
36         if (flags < 0) {
37                 return PATH_UNCHECKED;
38         }
39
40         if (!(flags & O_DIRECT)) {
41                 flags |= O_DIRECT;
42                 if (fcntl(fd,F_SETFL,flags) < 0) {
43                         return PATH_UNCHECKED;
44                 }
45                 reset_flags = 1;
46         }
47
48         while ( (res = read(fd,buff,size)) < 0 && errno == EINTR );
49         if (res < 0) {
50                 if (errno == EINVAL) {
51                         /* O_DIRECT is not available */
52                         retval = PATH_UNCHECKED;
53                 } else if (errno == ENOMEM) {
54                         retval = PATH_UP;
55                 } else {
56                         retval = PATH_DOWN;
57                 }
58         } else {
59                 retval = PATH_UP;
60         }
61         
62         if (reset_flags) {
63                 flags &= ~O_DIRECT;
64                 /* No point in checking for errors */
65                 fcntl(fd,F_SETFL,flags);
66         }
67
68         return retval;
69 }
70
71 extern int
72 directio (int fd, char *msg, void **context)
73 {
74         unsigned char *buf, *ptr;
75         struct readsector0_checker_context * ctxt = NULL;
76         unsigned long pgsize, numsect;
77         int ret, blksize;
78
79         pgsize = getpagesize();
80         
81         /*
82          * caller passed in a context : use its address
83          */
84         if (context)
85                 ctxt = (struct readsector0_checker_context *) (*context);
86
87         /*
88          * passed in context is uninitialized or volatile context :
89          * initialize it
90          */
91         if (!ctxt) {
92                 ctxt = malloc(sizeof(struct readsector0_checker_context));
93                 memset(ctxt, 0, sizeof(struct readsector0_checker_context));
94
95                 if (!ctxt) {
96                         MSG("cannot allocate context");
97                         return -1;
98                 }
99                 if (context)
100                         *context = ctxt;
101         }
102         if (fd <= 0) {
103                 MSG("no usable fd");
104                 ret = -1;
105                 goto out;
106         }
107         
108         if (ioctl(fd, BLKGETSIZE, &numsect) < 0) {
109                 MSG("cannot get number of sectors, set default");
110                 numsect = 0;
111         }
112
113         if (ioctl(fd, BLKBSZGET, &blksize) < 0) {
114                 MSG("cannot get blocksize, set default");
115                 blksize = 512;
116         }
117
118         if (blksize > 4096) {
119                 /*
120                  * Sanity check for DASD; BSZGET is broken
121                  */
122                 blksize = 4096;
123         }
124
125         if (!blksize) {
126                 /*
127                  * Blocksize is 0, assume we can't write
128                  * to this device.
129                  */
130                 MSG(MSG_DIRECTIO_DOWN);
131                 ret = PATH_DOWN;
132                 goto out;
133         }
134
135         buf = (unsigned char *)malloc(blksize + pgsize);
136         if (!buf){
137                 goto out;
138         }
139         ptr = (unsigned char *)(((unsigned long)buf + pgsize - 1) &
140                                 (~(pgsize - 1))); 
141         ret = direct_read(fd, ptr, blksize);
142
143         switch (ret)
144         {
145         case PATH_UNCHECKED:
146                 MSG(MSG_DIRECTIO_UNKNOWN);
147                 break;
148         case PATH_DOWN:
149                 MSG(MSG_DIRECTIO_DOWN);
150                 break;
151         case PATH_UP:
152                 MSG(MSG_DIRECTIO_UP);
153                 break;
154         default:
155                 break;
156         }
157         free(buf);
158
159 out:
160         /*
161          * caller told us he doesn't want to keep the context :
162          * free it
163          */
164         if (!context)
165                 free(ctxt);
166
167         return ret;
168 }