~ubuntu-branches/ubuntu/saucy/s390-tools/saucy

« back to all changes in this revision

Viewing changes to libu2s/u2s.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2008-07-15 23:55:41 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20080715235541-r79vu6eqh4qim413
Tags: 1.6.2-1
* New upstream version.
* Install udev rules.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * File...........: u2s.c
3
3
 * Author(s)......: Gerhard Tonn <ton@de.ibm.com>
4
4
 *                  Stefan Weinhuber <wein@de.ibm.com>
5
 
 * Bugreports.to..: <Linux390@de.ibm.com>
6
5
 *
7
 
 * (C) Copyright IBM Corp. 2004
 
6
 * Copyright IBM Corp. 2004, 2006.
8
7
 *
9
8
 */
10
9
 
13
12
#include <wait.h>
14
13
#include <errno.h>
15
14
#include <string.h>
16
 
#include <sysfs/libsysfs.h>
17
15
#include <sys/stat.h>
 
16
#include <sys/types.h>
 
17
#include <fcntl.h>
 
18
#include <sys/sysmacros.h>
 
19
#include <dirent.h>
18
20
 
19
21
#include "u2s.h"
20
22
 
21
23
#define DEV_BUFFER_LENGTH 20
22
 
 
23
 
 
24
 
/*
25
 
 * helper function that compares the string dev against the
26
 
 * attribute "dev" in directory dir
27
 
 * returns 1 if match is found, else 0
28
 
 */
29
 
int 
30
 
dir_matches_device(const char *dir, const char *dev) {
31
 
        int dev_attr_len, dev_parm_len, ret = 0;
32
 
        struct sysfs_attribute *devattr;
33
 
        char path[SYSFS_PATH_MAX];
34
 
 
35
 
        strcpy(path, dir);
36
 
        strcat(path, "/dev");
37
 
        devattr = sysfs_open_attribute(path);
38
 
        if (devattr) {
39
 
                sysfs_read_attribute(devattr);
40
 
                /* exclude white space from comparison */
41
 
                dev_attr_len = strspn(devattr->value, "1234567890:");
42
 
                dev_parm_len = strlen(dev);
43
 
                if (dev_attr_len != dev_parm_len )
44
 
                        ret = 0;
45
 
                else if (!strncmp(dev,devattr->value, dev_parm_len))
46
 
                        ret = 1;
47
 
                sysfs_close_attribute(devattr);
 
24
#define PATH_BUFFER_LENGTH 256
 
25
 
 
26
#define BLOCKPATH "/sys/block/"
 
27
#define DEVICE_LINK "device"
 
28
#define DEV_ATTRIBUTE "dev"
 
29
 
 
30
 
 
31
/*
 
32
 * Helper function that expects a file name and returns 1 if this
 
33
 * is a directory or 0 otherwise.
 
34
 */
 
35
static int isdir(char *name) {
 
36
 
 
37
        struct stat statbuf;
 
38
 
 
39
        if (lstat(name, &statbuf) < 0)
 
40
                return 0;
 
41
        return S_ISDIR(statbuf.st_mode);
 
42
}
 
43
 
 
44
/*
 
45
 * Helper function that expects a directory name in sysfs of the form
 
46
 * /sys/block/<devname>/ or /sys/block/<devname>/<partname>/.
 
47
 * It will try to read the file "dev" in this directory and compare
 
48
 * it's contents with the given dev string of the form <major>:<minor>.
 
49
 * Trailing white space (newline) is ignored.
 
50
 * The buffer name is expected to be long enough to hold the additional "dev".
 
51
 * Returns 1 if the directory matches dev, 0 otherwise.
 
52
 */
 
53
static int check_directory(char *name, char *dev) {
 
54
 
 
55
        char buffer[DEV_BUFFER_LENGTH];
 
56
        char *end;
 
57
        int fd;
 
58
        ssize_t count;
 
59
        int dev_attr_len, dev_parm_len, namelen;
 
60
 
 
61
        namelen = strlen(name);
 
62
        if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEV_ATTRIBUTE))
 
63
                return 0;
 
64
        end = name + namelen;
 
65
        strcpy(end, DEV_ATTRIBUTE);
 
66
        fd = open(name, O_RDONLY);
 
67
        *end = 0;
 
68
        if (fd < 0)
 
69
                return 0;
 
70
        count = read(fd, buffer, DEV_BUFFER_LENGTH);
 
71
        close(fd);
 
72
        if (count < 0)
 
73
                return 0;
 
74
        dev_attr_len = strspn(buffer, "1234567890:");
 
75
        dev_parm_len = strlen(dev);
 
76
        if (dev_attr_len != dev_parm_len )
 
77
                return 0;
 
78
        return (strncmp(dev, buffer, dev_parm_len) == 0);
 
79
}
 
80
 
 
81
/*
 
82
 * Helper function that expects a directory name in sysfs of the form
 
83
 * /sys/block/<devname>/. It will try to read a link "device"
 
84
 * in this directory and extract the busid, which is the last part
 
85
 * of that link. The buffer name is expected to be long enough
 
86
 * to hold the additional "device".
 
87
 * name: block device path in sysfs.
 
88
 * busid: buffer in which the busid string will be returned
 
89
 * returns 0 for successfull operation and -1 in case of an error.
 
90
 */
 
91
static int extract_busid(char *name, char *busid) {
 
92
 
 
93
        int count, namelen;
 
94
        char linkbuffer[PATH_BUFFER_LENGTH];
 
95
        char *start, *end;
 
96
 
 
97
        namelen = strlen(name);
 
98
        if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEVICE_LINK))
 
99
                return 0;
 
100
        end = name + namelen;
 
101
        strcpy(end, DEVICE_LINK);
 
102
        count = readlink(name, linkbuffer, PATH_BUFFER_LENGTH - 1);
 
103
        if (count < 0)
 
104
                return -1;
 
105
        linkbuffer[count] = 0;
 
106
        start = strrchr(linkbuffer, '/');
 
107
        if (!start)
 
108
                return -1;
 
109
        start++;
 
110
        strcpy(busid, start);
 
111
        return 0;
 
112
};
 
113
 
 
114
/*
 
115
 * Helper function that makes some basic checks on a directory entry.
 
116
 * The function checks if there is still enough space left in the buffer
 
117
 * for the new string, excludes '.' and '..', and verifies that the entry
 
118
 * is actually a directory.
 
119
 * buffer: the beginning of the name buffer
 
120
 * oldend: the current end of the string in the name buffer
 
121
 * dir: the dirent in question
 
122
 * returns: a pointer to the new end of the string in buffer or NULL if
 
123
 * one of the checks failed
 
124
 */
 
125
 
 
126
static char *append_if_directory(char *buffer, char *oldend, struct dirent *dir) {
 
127
 
 
128
        char *newend;
 
129
        int oldlength, dirlength;
 
130
 
 
131
        if (strcmp(dir->d_name, ".") == 0 ||
 
132
            strcmp(dir->d_name, "..") == 0)
 
133
                return NULL;
 
134
        oldlength = strlen(buffer);
 
135
        dirlength = strlen(dir->d_name);
 
136
        if (PATH_BUFFER_LENGTH < oldlength + dirlength + 2)
 
137
                return NULL;
 
138
        strcpy(oldend, dir->d_name);
 
139
        if (!isdir(buffer)) {
 
140
                *oldend = 0;
 
141
                return NULL;
48
142
        }
49
 
        return ret;
 
143
        newend = oldend + dirlength;
 
144
        strcpy(newend, "/");
 
145
        newend++;
 
146
 
 
147
        return newend;
50
148
}
51
149
 
52
 
 
53
150
/*
54
151
 * helper function that searches for a specific block device and returns
55
152
 * it's busid
56
 
 * sysfs: structure that represents the sysfs root directory
57
153
 * dev: <major>:<minor> of the device
58
154
 * busid: buffer in which the busid string will be returned
59
 
 * returns 0 for successfull operation and -1 in case of an error. 
 
155
 * returns 0 for successfull operation and -1 in case of an error.
60
156
 */
61
 
 
62
 
int 
63
 
find_busid_in_sysfs(const char *sysfs, char *dev, char *busid) {
64
 
 
65
 
        char *dir, *subdir, *blkdevdir;
66
 
        struct dlist *blockdirs, *blocksubdirs;
67
 
        char path[SYSFS_PATH_MAX], path1[SYSFS_PATH_MAX];
68
 
 
69
 
        strcpy(path, sysfs);
70
 
        strcat(path, "/");
71
 
        strcat(path, SYSFS_BLOCK_NAME);
72
 
 
73
 
        blockdirs = sysfs_open_directory_list(path);
74
 
        if (!blockdirs) 
75
 
                return -1;
76
 
        /* go through all directories and subdirectories in /sys/block
77
 
         * e.g. /sys/block/dasda as well as /sys/block/dasda1 etc.
78
 
         * match the attribute 'dev' against the string we build in
79
 
         * the first step.
 
157
static int find_busid_in_sysfs(char *dev, char *busid) {
 
158
 
 
159
        DIR *blockdir, *diskdir;
 
160
        struct dirent *blockde, *diskde;
 
161
        int found = 0;
 
162
        char namebuffer[PATH_BUFFER_LENGTH];
 
163
        char *blockend, *diskend = NULL, *partend;
 
164
 
 
165
        /* everything, including the other helper functions, works on the
 
166
         * same buffer area 'namebuffer'. The pointers blockend, diskend
 
167
         * and partend point to the end of the various names.
 
168
         * Example:
 
169
         * "/sys/block/dasda/dasda1/"
 
170
         *             ^ blockend
 
171
         *                   ^ diskend
 
172
         *                          ^ partend
80
173
         */
81
 
        blkdevdir=NULL;
82
 
        dlist_for_each_data(blockdirs, dir, char) {
83
 
                blocksubdirs = sysfs_open_directory_list(dir);
84
 
                if (blocksubdirs) {
85
 
                        dlist_for_each_data(blocksubdirs, subdir, char) {
86
 
                                strcpy(path1, path);
87
 
                                strcat(path1, "/");
88
 
                                strcat(path1, dir);
89
 
                                strcat(path1, "/");
90
 
                                strcat(path1, subdir);
91
 
                                if (dir_matches_device(path1, dev)) {
92
 
                                        blkdevdir = path1;
93
 
                                        break;
94
 
                                }
95
 
                        }
96
 
                }
97
 
                if (blkdevdir)
98
 
                        break;
99
 
                strcpy(path1, path);
100
 
                strcat(path1, "/");
101
 
                strcat(path1, dir);
102
 
                if (dir_matches_device(path1, dev)) {
103
 
                        blkdevdir = path1;
104
 
                        break;
105
 
                }
 
174
 
 
175
        strcpy(namebuffer,BLOCKPATH);
 
176
        blockdir = opendir(namebuffer);
 
177
        if (!blockdir)
 
178
                return -1;
 
179
        blockend = namebuffer + strlen(namebuffer);
 
180
        /* check each entry in /sys/block */
 
181
        while ((blockde = readdir(blockdir))) {
 
182
                diskend = append_if_directory(namebuffer, blockend, blockde);
 
183
                if (!diskend)
 
184
                        continue;
 
185
                found = check_directory(namebuffer, dev);
 
186
                if (found)
 
187
                        break;
 
188
                diskdir = opendir(namebuffer);
 
189
                if (!diskdir)
 
190
                        continue;
 
191
                /* check each entry in /sys/block/<disk name> */
 
192
                while ((diskde = readdir(diskdir))) {
 
193
                        partend = append_if_directory(
 
194
                                namebuffer, diskend, diskde);
 
195
                        if (!partend)
 
196
                                continue;
 
197
                        found = check_directory(namebuffer, dev);
 
198
                        if (found)
 
199
                                break;
 
200
                }
 
201
                closedir(diskdir);
 
202
                if (found)
 
203
                        break;
106
204
        }
107
 
        if (!blkdevdir) 
108
 
                return -1;
109
 
        /*
110
 
         * now that we have the right block device, we can follow the
111
 
         * link to the device and extract the busid
112
 
         */
113
 
        strcpy(path, blkdevdir);
114
 
        strcat(path, "/device");
115
 
        sysfs_get_link(path, path1, SYSFS_PATH_MAX);
116
 
        if (sysfs_get_name_from_path(path1, busid, SYSFS_BUS_ID_SIZE)) 
117
 
                return -1;
118
 
        return 0;
 
205
        closedir(blockdir);
 
206
        if (found) {
 
207
                *diskend = 0; /* remove partition directory from name */
 
208
                return extract_busid(namebuffer, busid);
 
209
        } else
 
210
                return -1;
119
211
}
120
212
 
121
213
/*
123
215
 * Works only for block devices.
124
216
 * devicenode: path to the device node
125
217
 * busid: buffer in which the busid string will be returned
126
 
 * returns 0 for successfull operation and -1 in case of an error. 
 
218
 * returns 0 for successfull operation and -1 in case of an error.
127
219
 */
128
 
 
129
 
int
130
 
u2s_getbusid(char *devicenode, char *busid)
 
220
int u2s_getbusid(char *devicenode, char *busid)
131
221
{
132
 
        char mnt_path[SYSFS_PATH_MAX];
133
222
        int maj, min, rc;
134
223
        struct stat stat_buf;
135
224
        char dev_string[DEV_BUFFER_LENGTH];
147
236
        min = minor(stat_buf.st_rdev);
148
237
        snprintf(dev_string,DEV_BUFFER_LENGTH,"%u:%u", maj, min);
149
238
 
150
 
        /* start navigation in sysfs and go to /sys/block */
151
 
        if(sysfs_get_mnt_path(mnt_path, SYSFS_PATH_MAX))
152
 
                return -1;
153
 
        rc = find_busid_in_sysfs(mnt_path, dev_string, busid);
 
239
        rc = find_busid_in_sysfs(dev_string, busid);
154
240
 
155
241
        return rc;
156
242
}
157