ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/libeio/eio.c
(Generate patch)

Comparing libeio/eio.c (file contents):
Revision 1.92 by root, Sun Jul 17 18:27:17 2011 UTC vs.
Revision 1.99 by root, Tue Jul 26 11:07:08 2011 UTC

71#endif 71#endif
72#ifndef ELOOP 72#ifndef ELOOP
73# define ELOOP EDOM 73# define ELOOP EDOM
74#endif 74#endif
75 75
76#if !defined(ENOTSOCK) && defined(WSAENOTSOCK)
77# define ENOTSOCK WSAENOTSOCK
78#endif
79
76static void eio_destroy (eio_req *req); 80static void eio_destroy (eio_req *req);
77 81
78#ifndef EIO_FINISH 82#ifndef EIO_FINISH
79# define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0 83# define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0
80#endif 84#endif
103 #undef PAGESIZE 107 #undef PAGESIZE
104 #define PAGESIZE 4096 /* GetSystemInfo? */ 108 #define PAGESIZE 4096 /* GetSystemInfo? */
105 109
106 #ifdef EIO_STRUCT_STATI64 110 #ifdef EIO_STRUCT_STATI64
107 #define stat(path,buf) _stati64 (path,buf) 111 #define stat(path,buf) _stati64 (path,buf)
108 #define fstat(fd,buf) _fstati64 (path,buf) 112 #define fstat(fd,buf) _fstati64 (fd,buf)
109 #endif 113 #endif
110 #define lstat(path,buf) stat (path,buf) 114 #define lstat(path,buf) stat (path,buf)
111 #define fsync(fd) (FlushFileBuffers ((HANDLE)EIO_FD_TO_WIN32_HANDLE (fd)) ? 0 : EIO_ERRNO (EBADF, -1)) 115 #define fsync(fd) (FlushFileBuffers ((HANDLE)EIO_FD_TO_WIN32_HANDLE (fd)) ? 0 : EIO_ERRNO (EBADF, -1))
112 #define mkdir(path,mode) _mkdir (path) 116 #define mkdir(path,mode) _mkdir (path)
113 #define link(old,neu) (CreateHardLink (neu, old, 0) ? 0 : EIO_ERRNO (ENOENT, -1)) 117 #define link(old,neu) (CreateHardLink (neu, old, 0) ? 0 : EIO_ERRNO (ENOENT, -1))
125 #define sync() EIO_ENOSYS () 129 #define sync() EIO_ENOSYS ()
126 #define readlink(path,buf,s) EIO_ENOSYS () 130 #define readlink(path,buf,s) EIO_ENOSYS ()
127 #define statvfs(path,buf) EIO_ENOSYS () 131 #define statvfs(path,buf) EIO_ENOSYS ()
128 #define fstatvfs(fd,buf) EIO_ENOSYS () 132 #define fstatvfs(fd,buf) EIO_ENOSYS ()
129 133
134 /* rename() uses MoveFile, which fails to overwrite */
135 #define rename(old,neu) eio__rename (old, neu)
136
137 static int
138 eio__rename (const char *old, const char *neu)
139 {
140 if (MoveFileEx (old, neu, MOVEFILE_REPLACE_EXISTING))
141 return 0;
142
143 /* should steal _dosmaperr */
144 switch (GetLastError ())
145 {
146 case ERROR_FILE_NOT_FOUND:
147 case ERROR_PATH_NOT_FOUND:
148 case ERROR_INVALID_DRIVE:
149 case ERROR_NO_MORE_FILES:
150 case ERROR_BAD_NETPATH:
151 case ERROR_BAD_NET_NAME:
152 case ERROR_BAD_PATHNAME:
153 case ERROR_FILENAME_EXCED_RANGE:
154 errno = ENOENT;
155 break;
156
157 default:
158 errno = EACCES;
159 break;
160 }
161
162 return -1;
163 }
164
130 /* we could even stat and see if it exists */ 165 /* we could even stat and see if it exists */
131 static int 166 static int
132 symlink (const char *old, const char *neu) 167 symlink (const char *old, const char *neu)
133 { 168 {
134 #if WINVER >= 0x0600 169 #if WINVER >= 0x0600
160 195
161 #include <sys/time.h> 196 #include <sys/time.h>
162 #include <sys/select.h> 197 #include <sys/select.h>
163 #include <sys/statvfs.h> 198 #include <sys/statvfs.h>
164 #include <unistd.h> 199 #include <unistd.h>
165 #include <utime.h>
166 #include <signal.h> 200 #include <signal.h>
167 #include <dirent.h> 201 #include <dirent.h>
168 202
169 #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES 203 #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES
170 #include <sys/mman.h> 204 #include <sys/mman.h>
192 226
193 #ifndef EIO_STRUCT_DIRENT 227 #ifndef EIO_STRUCT_DIRENT
194 #define EIO_STRUCT_DIRENT struct dirent 228 #define EIO_STRUCT_DIRENT struct dirent
195 #endif 229 #endif
196 230
231#endif
232
233#if HAVE_UTIMES
234# include <utime.h>
235#endif
236
237#if HAVE_SYS_SYSCALL_H
238# include <sys/syscall.h>
239#endif
240
241#if HAVE_SYS_PRCTL_H
242# include <sys/prctl.h>
197#endif 243#endif
198 244
199#if HAVE_SENDFILE 245#if HAVE_SENDFILE
200# if __linux 246# if __linux
201# include <sys/sendfile.h> 247# include <sys/sendfile.h>
233 279
234/* buffer size for various temporary buffers */ 280/* buffer size for various temporary buffers */
235#define EIO_BUFSIZE 65536 281#define EIO_BUFSIZE 65536
236 282
237#define dBUF \ 283#define dBUF \
238 char *eio_buf; \
239 ETP_WORKER_LOCK (self); \
240 self->dbuf = eio_buf = malloc (EIO_BUFSIZE); \ 284 char *eio_buf = malloc (EIO_BUFSIZE); \
241 ETP_WORKER_UNLOCK (self); \
242 errno = ENOMEM; \ 285 errno = ENOMEM; \
243 if (!eio_buf) \ 286 if (!eio_buf) \
244 return -1; 287 return -1
288
289#define FUBd \
290 free (eio_buf)
245 291
246#define EIO_TICKS ((1000000 + 1023) >> 10) 292#define EIO_TICKS ((1000000 + 1023) >> 10)
247 293
248#define ETP_PRI_MIN EIO_PRI_MIN 294#define ETP_PRI_MIN EIO_PRI_MIN
249#define ETP_PRI_MAX EIO_PRI_MAX 295#define ETP_PRI_MAX EIO_PRI_MAX
255static int eio_finish (eio_req *req); 301static int eio_finish (eio_req *req);
256#define ETP_FINISH(req) eio_finish (req) 302#define ETP_FINISH(req) eio_finish (req)
257static void eio_execute (struct etp_worker *self, eio_req *req); 303static void eio_execute (struct etp_worker *self, eio_req *req);
258#define ETP_EXECUTE(wrk,req) eio_execute (wrk,req) 304#define ETP_EXECUTE(wrk,req) eio_execute (wrk,req)
259 305
260#define ETP_WORKER_CLEAR(req) \
261 if (wrk->dbuf) \
262 { \
263 free (wrk->dbuf); \
264 wrk->dbuf = 0; \
265 }
266
267#define ETP_WORKER_COMMON \
268 void *dbuf;
269
270/*****************************************************************************/ 306/*****************************************************************************/
271 307
272#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) 308#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1)
273 309
274/* calculate time difference in ~1/EIO_TICKS of a second */ 310/* calculate time difference in ~1/EIO_TICKS of a second */
302/* 338/*
303 * make our pread/pwrite emulation safe against themselves, but not against 339 * make our pread/pwrite emulation safe against themselves, but not against
304 * normal read/write by using a mutex. slows down execution a lot, 340 * normal read/write by using a mutex. slows down execution a lot,
305 * but that's your problem, not mine. 341 * but that's your problem, not mine.
306 */ 342 */
307static xmutex_t preadwritelock = X_MUTEX_INIT; 343static xmutex_t preadwritelock;
308#endif 344#endif
309 345
310typedef struct etp_worker 346typedef struct etp_worker
311{ 347{
312 /* locked by wrklock */ 348 /* locked by wrklock */
315 xthread_t tid; 351 xthread_t tid;
316 352
317 /* locked by reslock, reqlock or wrklock */ 353 /* locked by reslock, reqlock or wrklock */
318 ETP_REQ *req; /* currently processed request */ 354 ETP_REQ *req; /* currently processed request */
319 355
356#ifdef ETP_WORKER_COMMON
320 ETP_WORKER_COMMON 357 ETP_WORKER_COMMON
358#endif
321} etp_worker; 359} etp_worker;
322 360
323static etp_worker wrk_first = { &wrk_first, &wrk_first, 0 }; /* NOT etp */ 361static etp_worker wrk_first; /* NOT etp */
324 362
325#define ETP_WORKER_LOCK(wrk) X_LOCK (wrklock) 363#define ETP_WORKER_LOCK(wrk) X_LOCK (wrklock)
326#define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock) 364#define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock)
327 365
328/* worker threads management */ 366/* worker threads management */
329 367
330static void ecb_cold 368static void ecb_cold
331etp_worker_clear (etp_worker *wrk) 369etp_worker_clear (etp_worker *wrk)
332{ 370{
333 ETP_WORKER_CLEAR (wrk);
334} 371}
335 372
336static void ecb_cold 373static void ecb_cold
337etp_worker_free (etp_worker *wrk) 374etp_worker_free (etp_worker *wrk)
338{ 375{
453 } 490 }
454 491
455 abort (); 492 abort ();
456} 493}
457 494
458static void ecb_cold 495static int ecb_cold
459etp_thread_init (void) 496etp_init (void (*want_poll)(void), void (*done_poll)(void))
460{ 497{
461#if !HAVE_PREADWRITE
462 X_MUTEX_CREATE (preadwritelock);
463#endif
464 X_MUTEX_CREATE (wrklock); 498 X_MUTEX_CREATE (wrklock);
465 X_MUTEX_CREATE (reslock); 499 X_MUTEX_CREATE (reslock);
466 X_MUTEX_CREATE (reqlock); 500 X_MUTEX_CREATE (reqlock);
467 X_COND_CREATE (reqwait); 501 X_COND_CREATE (reqwait);
468}
469 502
470static void ecb_cold
471etp_atfork_child (void)
472{
473 reqq_init (&req_queue); 503 reqq_init (&req_queue);
474 reqq_init (&res_queue); 504 reqq_init (&res_queue);
475 505
476 wrk_first.next = 506 wrk_first.next =
477 wrk_first.prev = &wrk_first; 507 wrk_first.prev = &wrk_first;
479 started = 0; 509 started = 0;
480 idle = 0; 510 idle = 0;
481 nreqs = 0; 511 nreqs = 0;
482 nready = 0; 512 nready = 0;
483 npending = 0; 513 npending = 0;
484
485 etp_thread_init ();
486}
487
488static void ecb_cold
489etp_once_init (void)
490{
491 etp_thread_init ();
492 X_THREAD_ATFORK (0, 0, etp_atfork_child);
493}
494
495static int ecb_cold
496etp_init (void (*want_poll)(void), void (*done_poll)(void))
497{
498 static pthread_once_t doinit = PTHREAD_ONCE_INIT;
499
500 pthread_once (&doinit, etp_once_init);
501 514
502 want_poll_cb = want_poll; 515 want_poll_cb = want_poll;
503 done_poll_cb = done_poll; 516 done_poll_cb = done_poll;
504 517
505 return 0; 518 return 0;
963#if !HAVE_FDATASYNC 976#if !HAVE_FDATASYNC
964# undef fdatasync 977# undef fdatasync
965# define fdatasync(fd) fsync (fd) 978# define fdatasync(fd) fsync (fd)
966#endif 979#endif
967 980
981static int
982eio__syncfs (int fd)
983{
984 int res;
985
986#if HAVE_SYS_SYNCFS
987 res = (int)syscall (__NR_syncfs, (int)(fd));
988#else
989 res = -1;
990 errno = ENOSYS;
991#endif
992
993 if (res < 0 && errno == ENOSYS && fd >= 0)
994 sync ();
995
996 return res;
997}
998
968/* sync_file_range always needs emulation */ 999/* sync_file_range always needs emulation */
969static int 1000static int
970eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags) 1001eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags)
971{ 1002{
972#if HAVE_SYNC_FILE_RANGE 1003#if HAVE_SYNC_FILE_RANGE
1021 pread (fd, eio_buf, len, offset); 1052 pread (fd, eio_buf, len, offset);
1022 offset += len; 1053 offset += len;
1023 todo -= len; 1054 todo -= len;
1024 } 1055 }
1025 1056
1057 FUBd;
1058
1026 errno = 0; 1059 errno = 0;
1027 return count; 1060 return count;
1028} 1061}
1029 1062
1030#endif 1063#endif
1031 1064
1032/* sendfile always needs emulation */ 1065/* sendfile always needs emulation */
1033static eio_ssize_t 1066static eio_ssize_t
1034eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) 1067eio__sendfile (int ofd, int ifd, off_t offset, size_t count)
1035{ 1068{
1036 eio_ssize_t written = 0; 1069 eio_ssize_t written = 0;
1037 eio_ssize_t res; 1070 eio_ssize_t res;
1038 1071
1039 if (!count) 1072 if (!count)
1175 1208
1176 offset += cnt; 1209 offset += cnt;
1177 res += cnt; 1210 res += cnt;
1178 count -= cnt; 1211 count -= cnt;
1179 } 1212 }
1213
1214 FUBd;
1180 } 1215 }
1181 1216
1182 return res; 1217 return res;
1183} 1218}
1184 1219
1670 1705
1671 if (dirp == INVALID_HANDLE_VALUE) 1706 if (dirp == INVALID_HANDLE_VALUE)
1672 { 1707 {
1673 dirp = 0; 1708 dirp = 0;
1674 1709
1710 /* should steal _dosmaperr */
1675 switch (GetLastError ()) 1711 switch (GetLastError ())
1676 { 1712 {
1677 case ERROR_FILE_NOT_FOUND: 1713 case ERROR_FILE_NOT_FOUND:
1678 req->result = 0; 1714 req->result = 0;
1679 break; 1715 break;
1913{ 1949{
1914 ETP_REQ *req; 1950 ETP_REQ *req;
1915 struct timespec ts; 1951 struct timespec ts;
1916 etp_worker *self = (etp_worker *)thr_arg; 1952 etp_worker *self = (etp_worker *)thr_arg;
1917 1953
1954#if HAVE_PRCTL_SET_NAME
1955 prctl (PR_SET_NAME, (unsigned long)"eio_thread", 0, 0, 0);
1956#endif
1957
1918 /* try to distribute timeouts somewhat evenly */ 1958 /* try to distribute timeouts somewhat evenly */
1919 ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL); 1959 ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL);
1920 1960
1921 for (;;) 1961 for (;;)
1922 { 1962 {
1991/*****************************************************************************/ 2031/*****************************************************************************/
1992 2032
1993int ecb_cold 2033int ecb_cold
1994eio_init (void (*want_poll)(void), void (*done_poll)(void)) 2034eio_init (void (*want_poll)(void), void (*done_poll)(void))
1995{ 2035{
2036#if !HAVE_PREADWRITE
2037 X_MUTEX_CREATE (preadwritelock);
2038#endif
2039
1996 return etp_init (want_poll, done_poll); 2040 return etp_init (want_poll, done_poll);
1997} 2041}
1998 2042
1999ecb_inline void 2043ecb_inline void
2000eio_api_destroy (eio_req *req) 2044eio_api_destroy (eio_req *req)
2045 case EIO_WRITE: req->result = req->offs >= 0 2089 case EIO_WRITE: req->result = req->offs >= 0
2046 ? pwrite (req->int1, req->ptr2, req->size, req->offs) 2090 ? pwrite (req->int1, req->ptr2, req->size, req->offs)
2047 : write (req->int1, req->ptr2, req->size); break; 2091 : write (req->int1, req->ptr2, req->size); break;
2048 2092
2049 case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break; 2093 case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break;
2050 case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size, self); break; 2094 case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size); break;
2051 2095
2052 case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2096 case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT));
2053 req->result = stat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break; 2097 req->result = stat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break;
2054 case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2098 case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT));
2055 req->result = lstat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break; 2099 req->result = lstat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break;
2085 req->result = readlink (req->ptr1, req->ptr2, PATH_MAX); break; 2129 req->result = readlink (req->ptr1, req->ptr2, PATH_MAX); break;
2086 2130
2087 case EIO_SYNC: req->result = 0; sync (); break; 2131 case EIO_SYNC: req->result = 0; sync (); break;
2088 case EIO_FSYNC: req->result = fsync (req->int1); break; 2132 case EIO_FSYNC: req->result = fsync (req->int1); break;
2089 case EIO_FDATASYNC: req->result = fdatasync (req->int1); break; 2133 case EIO_FDATASYNC: req->result = fdatasync (req->int1); break;
2134 case EIO_SYNCFS: req->result = eio__syncfs (req->int1); break;
2135 case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break;
2090 case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break; 2136 case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break;
2091 case EIO_MTOUCH: req->result = eio__mtouch (req); break; 2137 case EIO_MTOUCH: req->result = eio__mtouch (req); break;
2092 case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break; 2138 case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break;
2093 case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break; 2139 case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break;
2094 case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break;
2095 case EIO_FALLOCATE: req->result = eio__fallocate (req->int1, req->int2, req->offs, req->size); break; 2140 case EIO_FALLOCATE: req->result = eio__fallocate (req->int1, req->int2, req->offs, req->size); break;
2096 2141
2097 case EIO_READDIR: eio__scandir (req, self); break; 2142 case EIO_READDIR: eio__scandir (req, self); break;
2098 2143
2099 case EIO_BUSY: 2144 case EIO_BUSY:
2180eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 2225eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data)
2181{ 2226{
2182 REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; 2227 REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND;
2183} 2228}
2184 2229
2230eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data)
2231{
2232 REQ (EIO_FDATASYNC); req->int1 = fd; SEND;
2233}
2234
2235eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data)
2236{
2237 REQ (EIO_SYNCFS); req->int1 = fd; SEND;
2238}
2239
2240eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data)
2241{
2242 REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND;
2243}
2244
2185eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 2245eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data)
2186{ 2246{
2187 REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; 2247 REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND;
2188} 2248}
2189 2249
2195eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) 2255eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data)
2196{ 2256{
2197 REQ (EIO_MLOCKALL); req->int1 = flags; SEND; 2257 REQ (EIO_MLOCKALL); req->int1 = flags; SEND;
2198} 2258}
2199 2259
2200eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data)
2201{
2202 REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND;
2203}
2204
2205eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) 2260eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data)
2206{ 2261{
2207 REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; 2262 REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND;
2208}
2209
2210eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data)
2211{
2212 REQ (EIO_FDATASYNC); req->int1 = fd; SEND;
2213} 2263}
2214 2264
2215eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) 2265eio_req *eio_close (int fd, int pri, eio_cb cb, void *data)
2216{ 2266{
2217 REQ (EIO_CLOSE); req->int1 = fd; SEND; 2267 REQ (EIO_CLOSE); req->int1 = fd; SEND;
2445/* misc garbage */ 2495/* misc garbage */
2446 2496
2447eio_ssize_t 2497eio_ssize_t
2448eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) 2498eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count)
2449{ 2499{
2450 etp_worker wrk;
2451 eio_ssize_t ret;
2452
2453 wrk.dbuf = 0;
2454
2455 ret = eio__sendfile (ofd, ifd, offset, count, &wrk); 2500 return eio__sendfile (ofd, ifd, offset, count);
2456
2457 if (wrk.dbuf)
2458 free (wrk.dbuf);
2459
2460 return ret;
2461} 2501}
2462 2502

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines