root/branches/Orange-Branch/src/kernel/linux-2.6/inode.c @ 8806

Revision 8806, 19.2 KB (checked in by mtmoore, 2 years ago)

fix compilation issues caused on older systems with new kernel changes

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 */
6
7/** \file
8 *  \ingroup pvfs2linux
9 *
10 *  Linux VFS inode operations.
11 */
12
13#include "pvfs2-kernel.h"
14#include "pvfs2-bufmap.h"
15#include "pvfs2-types.h"
16#include "pvfs2-internal.h"
17
18static int read_one_page(struct page *page)
19{
20    void *page_data;
21    int ret, max_block;
22    ssize_t bytes_read = 0;
23    struct inode *inode = page->mapping->host;
24    const uint32_t blocksize = PAGE_CACHE_SIZE;  /* inode->i_blksize */
25    const uint32_t blockbits = PAGE_CACHE_SHIFT; /* inode->i_blkbits */
26
27    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_readpage called with page %p\n",page);
28    page_data = pvfs2_kmap(page);
29
30    max_block = ((inode->i_size / blocksize) + 1);
31
32    if (page->index < max_block)
33    {
34        loff_t blockptr_offset =
35            (((loff_t)page->index) << blockbits);
36        bytes_read = pvfs2_inode_read(
37            inode, page_data, blocksize, &blockptr_offset, 0, inode->i_size);
38    }
39    /* only zero remaining unread portions of the page data */
40    if (bytes_read > 0)
41    {
42        memset(page_data + bytes_read, 0, blocksize - bytes_read);
43    }
44    else
45    {
46        memset(page_data, 0, blocksize);
47    }
48    /* takes care of potential aliasing */
49    flush_dcache_page(page);
50    if (bytes_read < 0)
51    {
52        ret = bytes_read;
53        SetPageError(page);
54    }
55    else
56    {
57        SetPageUptodate(page);
58        if (PageError(page))
59        {
60            ClearPageError(page);
61        }
62        ret = 0;
63    }
64    pvfs2_kunmap(page);
65    /* unlock the page after the ->readpage() routine completes */
66    unlock_page(page);
67    return ret;
68}
69
70static int pvfs2_readpage(
71    struct file *file,
72    struct page *page)
73{
74    return read_one_page(page);
75}
76
77#ifndef PVFS2_LINUX_KERNEL_2_4
78static int pvfs2_readpages(
79    struct file *file,
80    struct address_space *mapping,
81    struct list_head *pages,
82    unsigned nr_pages)
83{
84    int page_idx;
85    int ret;
86
87    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_readpages called\n");
88
89    for (page_idx = 0; page_idx < nr_pages; page_idx++)
90    {
91        struct page *page;
92        page = list_entry(pages->prev, struct page, lru);
93        list_del(&page->lru);
94        if (!add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) {
95            ret = read_one_page(page);
96        }
97        else {
98            page_cache_release(page);
99        }
100    }
101    BUG_ON(!list_empty(pages));
102    return 0;
103}
104
105#ifdef HAVE_INT_RETURN_ADDRESS_SPACE_OPERATIONS_INVALIDATEPAGE
106static int pvfs2_invalidatepage(struct page *page, unsigned long offset)
107#else
108static void pvfs2_invalidatepage(struct page *page, unsigned long offset)
109#endif
110{
111    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_invalidatepage called on page %p "
112                "(offset is %lu)\n", page, offset);
113
114    ClearPageUptodate(page);
115    ClearPageMappedToDisk(page);
116#ifdef HAVE_INT_RETURN_ADDRESS_SPACE_OPERATIONS_INVALIDATEPAGE
117    return 0;
118#else
119    return;
120#endif
121
122}
123
124#ifdef HAVE_INT_ARG2_ADDRESS_SPACE_OPERATIONS_RELEASEPAGE
125static int pvfs2_releasepage(struct page *page, int foo)
126#else
127static int pvfs2_releasepage(struct page *page, gfp_t foo)
128#endif
129{
130    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_releasepage called on page %p\n", page);
131    return 0;
132}
133
134struct backing_dev_info pvfs2_backing_dev_info =
135{
136#ifdef HAVE_BACKING_DEV_INFO_NAME
137    .name = "pvfs2",
138#endif
139    .ra_pages = 0,
140#ifdef HAVE_BDI_MEMORY_BACKED
141    /* old interface, up through 2.6.11 */
142    .memory_backed = 1 /* does not contribute to dirty memory */
143#else
144    .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
145#endif
146};
147#endif /* !PVFS2_LINUX_KERNEL_2_4 */
148
149/** PVFS2 implementation of address space operations */
150struct address_space_operations pvfs2_address_operations =
151{
152#ifdef PVFS2_LINUX_KERNEL_2_4
153    readpage : pvfs2_readpage
154#else
155    .readpage = pvfs2_readpage,
156    .readpages = pvfs2_readpages,
157    .invalidatepage = pvfs2_invalidatepage,
158    .releasepage = pvfs2_releasepage
159#endif
160};
161
162/** Change size of an object referenced by inode
163 */
164void pvfs2_truncate(struct inode *inode)
165{
166    loff_t orig_size = pvfs2_i_size_read(inode);
167
168    if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
169        return;
170    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2: pvfs2_truncate called on inode %llu "
171                "with size %ld\n", llu(get_handle_from_ino(inode)), (long) orig_size);
172
173    /* successful truncate when size changes also requires mtime updates
174     * although the mtime updates are propagated lazily!
175     */
176    if (pvfs2_truncate_inode(inode, inode->i_size) == 0
177            && (orig_size != pvfs2_i_size_read(inode)))
178    {
179        pvfs2_inode_t *pvfs2_inode = PVFS2_I(inode);
180        SetMtimeFlag(pvfs2_inode);
181        inode->i_mtime = CURRENT_TIME;
182        mark_inode_dirty_sync(inode);
183    }
184}
185
186/** Change attributes of an object referenced by dentry.
187 */
188int pvfs2_setattr(struct dentry *dentry, struct iattr *iattr)
189{
190    int ret = -EINVAL;
191    struct inode *inode = dentry->d_inode;
192
193    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_setattr: called on %s\n",
194                 dentry->d_name.name);
195
196    ret = inode_change_ok(inode, iattr);
197    if (ret == 0)
198    {
199
200#ifdef HAVE_INODE_SETATTR
201        ret = inode_setattr(inode, iattr);
202#else
203        if ((iattr->ia_valid & ATTR_SIZE) &&
204           iattr->ia_size != i_size_read(inode))
205        {
206            ret = vmtruncate(inode, iattr->ia_size);
207            if (ret)
208                return ret;
209        }
210
211        setattr_copy(inode, iattr);
212        mark_inode_dirty(inode);
213        ret = 0;
214#endif /* HAVE_INODE_SETATTR */
215   
216        gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_setattr: inode_setattr returned %d\n", ret);
217
218        if (ret == 0)
219        {
220            ret = pvfs2_inode_setattr(inode, iattr);
221#if !defined(PVFS2_LINUX_KERNEL_2_4) && defined(HAVE_GENERIC_GETXATTR) && defined(CONFIG_FS_POSIX_ACL)
222            if (!ret && (iattr->ia_valid & ATTR_MODE))
223            {
224                /* change mod on a file that has ACLs */
225                ret = pvfs2_acl_chmod(inode);
226            }
227#endif
228        }
229    }
230    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_setattr: returning %d\n", ret);
231    return ret;
232}
233
234#ifdef PVFS2_LINUX_KERNEL_2_4
235/** Linux 2.4 only equivalent of getattr
236 */
237int pvfs2_revalidate(struct dentry *dentry)
238{
239    int ret = 0;
240    struct inode *inode = (dentry ? dentry->d_inode : NULL);
241
242    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_revalidate: called on %s\n", dentry->d_name.name);
243
244    /*
245     * A revalidate expects that all fields of the inode would be refreshed
246     * So we have no choice but to refresh all attributes.
247     */
248    ret = pvfs2_inode_getattr(inode, PVFS_ATTR_SYS_ALL_NOHINT);
249    if (ret)
250    {
251        /* assume an I/O error and flag inode as bad */
252        gossip_debug(GOSSIP_INODE_DEBUG, "%s:%s:%d calling make bad inode\n", __FILE__,  __func__, __LINE__);
253        pvfs2_make_bad_inode(inode);
254    }
255    return ret;
256}
257#else
258/** Obtain attributes of an object given a dentry
259 */
260int pvfs2_getattr(
261    struct vfsmount *mnt,
262    struct dentry *dentry,
263    struct kstat *kstat)
264{
265    int ret = -ENOENT;
266    struct inode *inode = dentry->d_inode;
267    pvfs2_inode_t *pvfs2_inode = NULL;
268
269    gossip_debug(GOSSIP_INODE_DEBUG,
270        "pvfs2_getattr: called on %s\n", dentry->d_name.name);
271
272    /* This seems to be the only place to reliably detect mount options
273     * parsed by the VFS layer.  Propigate them to our internal sb structure so
274     * that we can handle lazy time updates properly.
275     */
276#ifdef HAVE_MNT_NOATIME
277    if(mnt->mnt_flags && MNT_NOATIME)
278    {
279        inode->i_sb->s_flags |= MS_NOATIME;
280    }
281#endif
282#ifdef HAVE_MNT_NODIRATIME
283    if(mnt->mnt_flags && MNT_NODIRATIME)
284    {
285        inode->i_sb->s_flags |= MS_NODIRATIME;
286    }
287#endif
288
289    /*
290     * Similar to the above comment, a getattr also expects that all fields/attributes
291     * of the inode would be refreshed. So again, we dont have too much of a choice
292     * but refresh all the attributes.
293     */
294    ret = pvfs2_inode_getattr(inode, PVFS_ATTR_SYS_ALL_NOHINT);
295    if (ret == 0)
296    {
297        generic_fillattr(inode, kstat);
298        /* override block size reported to stat */
299        pvfs2_inode = PVFS2_I(inode);
300        kstat->blksize = pvfs2_inode->blksize;
301    }
302    else
303    {
304        /* assume an I/O error and flag inode as bad */
305        gossip_debug(GOSSIP_INODE_DEBUG, "%s:%s:%d calling make bad inode\n", __FILE__,  __func__, __LINE__);
306        pvfs2_make_bad_inode(inode);
307    }
308    return ret;
309}
310
311#ifdef HAVE_GETATTR_LITE_INODE_OPERATIONS
312
313uint32_t convert_to_pvfs2_mask(unsigned long lite_mask)
314{
315    uint32_t mask = 0;
316
317    if (SLITE_SIZET(lite_mask))
318        mask |= PVFS_ATTR_SYS_SIZE;
319    if (SLITE_ATIME(lite_mask))
320        mask |= PVFS_ATTR_SYS_ATIME;
321    if (SLITE_MTIME(lite_mask))
322        mask |= PVFS_ATTR_SYS_MTIME;
323    if (SLITE_CTIME(lite_mask))
324        mask |= PVFS_ATTR_SYS_CTIME;
325    return mask;
326}
327
328int pvfs2_getattr_lite(
329    struct vfsmount *mnt,
330    struct dentry *dentry,
331    struct kstat_lite *kstat_lite)
332{
333    int ret = -ENOENT;
334    struct inode *inode = dentry->d_inode;
335    uint32_t mask;
336
337    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_getattr_lite: called on %s\n", dentry->d_name.name);
338
339    /*
340     * ->getattr_lite needs to refresh only certain fields
341     * of the inode and that is indicated by the lite_mask
342     * field of kstat_lite structure.
343     */
344    mask = convert_to_pvfs2_mask(kstat_lite->lite_mask);
345    ret = pvfs2_inode_getattr(inode, mask);
346    if (ret == 0)
347    {
348        generic_fillattr_lite(inode, kstat_lite);
349    }
350    else
351    {
352        /* assume an I/O error and flag inode as bad */
353        gossip_debug(GOSSIP_INODE_DEBUG, "%s:%s:%d calling make bad inode\n", __FILE__,  __func__, __LINE__);
354        pvfs2_make_bad_inode(inode);
355    }
356    return ret;
357}
358#endif
359#endif /* PVFS2_LINUX_KERNEL_2_4 */
360
361/** PVFS2 implementation of VFS inode operations for files */
362struct inode_operations pvfs2_file_inode_operations =
363{
364#ifdef PVFS2_LINUX_KERNEL_2_4
365    truncate : pvfs2_truncate,
366    setattr : pvfs2_setattr,
367    revalidate : pvfs2_revalidate,
368#ifdef HAVE_XATTR
369    setxattr : pvfs2_setxattr,
370    getxattr : pvfs2_getxattr,
371    removexattr: pvfs2_removexattr,
372    listxattr: pvfs2_listxattr,
373#endif
374#else
375    .truncate = pvfs2_truncate,
376    .setattr = pvfs2_setattr,
377    .getattr = pvfs2_getattr,
378#ifdef HAVE_GETATTR_LITE_INODE_OPERATIONS
379    .getattr_lite = pvfs2_getattr_lite,
380#endif
381#if defined(HAVE_GENERIC_GETXATTR) && defined(CONFIG_FS_POSIX_ACL)
382    .setxattr = generic_setxattr,
383    .getxattr = generic_getxattr,
384    .removexattr = generic_removexattr,
385#else
386    .setxattr = pvfs2_setxattr,
387    .getxattr = pvfs2_getxattr,
388    .removexattr = pvfs2_removexattr,
389#endif
390    .listxattr = pvfs2_listxattr,
391#if defined(HAVE_GENERIC_GETXATTR) && defined(CONFIG_FS_POSIX_ACL)
392    .permission = pvfs2_permission,
393#endif
394#ifdef HAVE_FILL_HANDLE_INODE_OPERATIONS
395    .fill_handle = pvfs2_fill_handle,
396#endif
397#endif
398};
399
400#if defined(HAVE_IGET5_LOCKED) || defined (HAVE_IGET4_LOCKED)
401
402/*
403 * Given a PVFS2 object identifier (fsid, handle), convert it into a ino_t type
404 * that will be used as a hash-index from where the handle will
405 * be searched for in the VFS hash table of inodes.
406 */
407static inline ino_t pvfs2_handle_hash(PVFS_object_ref *ref)
408{
409    if (!ref)
410        return 0;
411    return pvfs2_handle_to_ino(ref->handle);
412}
413
414/* the ->set callback of iget5_locked and friends. Sorta equivalent to the ->read_inode()
415 * callback if we are using iget and friends
416 */
417int pvfs2_set_inode(struct inode *inode, void *data)
418{
419    /* callbacks to set inode number handle */
420    PVFS_object_ref *ref = (PVFS_object_ref *) data;
421    pvfs2_inode_t *pvfs2_inode = NULL;
422
423    /* Make sure that we have sane parameters */
424    if (!data || !inode)
425        return 0;
426    pvfs2_inode = PVFS2_I(inode);
427    if (!pvfs2_inode)
428        return 0;
429    pvfs2_inode_initialize(pvfs2_inode);
430    pvfs2_inode->refn.fs_id  = ref->fs_id;
431    pvfs2_inode->refn.handle = ref->handle;
432    return 0;
433}
434
435#ifdef HAVE_IGET5_LOCKED
436static int
437pvfs2_test_inode(struct inode *inode, void *data)
438#elif defined(HAVE_IGET4_LOCKED)
439static int
440pvfs2_test_inode(struct inode *inode, unsigned long ino, void *data)
441#endif
442{
443    /* callbacks to determine if handles match */
444    PVFS_object_ref *ref = (PVFS_object_ref *) data;
445    pvfs2_inode_t *pvfs2_inode = NULL;
446
447    pvfs2_inode = PVFS2_I(inode);
448    return (pvfs2_inode->refn.handle == ref->handle && pvfs2_inode->refn.fs_id == ref->fs_id);
449}
450#endif
451
452/*
453 * Front-end to lookup the inode-cache maintained by the VFS using the PVFS2
454 * file handle instead of the inode number.
455 * Problem with iget() is well-documented in that it can lead to possible
456 * collissions especially for a file-system with 64 bit handles since inode->i_ino
457 * is only a scalar field (32 bits). So the trick now is to use iget4_locked (OR) iget5_locked
458 * if the kernel defines one and set inode number to be just a hash for the
459 * handle
460 * @sb: the file system super block instance
461 * @ref: The PVFS2 object for which we are trying to locate an inode structure
462 * @keep_locked : indicates whether the inode must be simply allocated and not filled
463 * in with the results from a ->getattr. i.e. if keep_locked is set to 0, we do a getattr() and
464 * unlock the inode and if set to 1, we do not issue a getattr() and keep it locked
465 *
466 * Boy, this function is so ugly with all these macros. I wish I could find a better
467 * way to reduce the macro clutter.
468 */
469struct inode *pvfs2_iget_common(struct super_block *sb, PVFS_object_ref *ref, int keep_locked)
470{
471    struct inode *inode = NULL;
472    unsigned long hash;
473
474#if defined(HAVE_IGET5_LOCKED) || defined(HAVE_IGET4_LOCKED)
475    hash = pvfs2_handle_hash(ref);
476#if defined(HAVE_IGET5_LOCKED)
477    inode = iget5_locked(sb, hash, pvfs2_test_inode, pvfs2_set_inode, ref);
478#elif defined(HAVE_IGET4_LOCKED)
479    inode = iget4_locked(sb, hash, pvfs2_test_inode, ref);
480#endif
481#else
482    hash = (unsigned long) ref->handle;
483#ifdef HAVE_IGET_LOCKED
484    inode = iget_locked(sb, hash);
485#else
486    /* iget() internally issues a call to read_inode() */
487    inode = iget(sb, hash);
488#endif
489#endif
490    if (!keep_locked)
491    {
492#if defined(HAVE_IGET5_LOCKED) || defined(HAVE_IGET4_LOCKED) || defined(HAVE_IGET_LOCKED)
493        if (inode && (inode->i_state & I_NEW))
494        {
495            inode->i_ino = hash; /* needed for stat etc */
496            /* iget4_locked and iget_locked dont invoke the set_inode callback.
497             * So we work around that by stashing the pvfs object reference
498             * in the inode specific private part for 2.4 kernels and invoking
499             * the setcallback explicitly for 2.6 kernels.
500             */
501#if defined(HAVE_IGET4_LOCKED) || defined(HAVE_IGET_LOCKED)
502            if (PVFS2_I(inode)) {
503                pvfs2_set_inode(inode, ref);
504            }
505            else {
506#ifdef PVFS2_LINUX_KERNEL_2_4
507                inode->u.generic_ip = (void *) ref;
508#endif
509            }
510#endif
511            /* issue a call to read the inode */
512            pvfs2_read_inode(inode);
513            unlock_new_inode(inode);
514        }
515#endif
516    }
517    gossip_debug(GOSSIP_INODE_DEBUG, "iget handle %llu, fsid %d hash %ld i_ino %lu\n",
518                 ref->handle, ref->fs_id, hash, inode->i_ino);
519    return inode;
520}
521
522/** Allocates a Linux inode structure with additional PVFS2-specific
523 *  private data (I think -- RobR).
524 */
525struct inode *pvfs2_get_custom_inode_common(
526    struct super_block *sb,
527    struct inode *dir,
528    int mode,
529    dev_t dev,
530    PVFS_object_ref object,
531    int from_create)
532{
533    struct inode *inode = NULL;
534    pvfs2_inode_t *pvfs2_inode = NULL;
535
536    gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_get_custom_inode_common: called\n  (sb is %p | "
537                "MAJOR(dev)=%u | MINOR(dev)=%u)\n", sb, MAJOR(dev),
538                MINOR(dev));
539
540    inode = pvfs2_iget(sb, &object);
541    if (inode)
542    {
543        /* initialize pvfs2 specific private data */
544        pvfs2_inode = PVFS2_I(inode);
545        if (!pvfs2_inode)
546        {
547            iput(inode);
548            gossip_err("pvfs2_get_custom_inode: PRIVATE "
549                        "DATA NOT ALLOCATED\n");
550            return NULL;
551        }
552
553        /*
554         * Since we are using the same function to create a new on-disk object
555         * as well as to create an in-memory object, the mode of the object
556         * needs to be set carefully. If we are called from a function that is
557         * creating a new on-disk object, set its mode here since the caller is
558         * providing it. Else let it be since the getattr should fill it up
559         * properly.
560         */
561        if (from_create)
562        {
563            /* the exception is when we are creating a directory that needs
564             * to inherit the setgid bit.  That much we need to preserve from
565             * the getattr's view of the mode.
566             */
567            if(inode->i_mode & S_ISGID)
568            {
569                gossip_debug(GOSSIP_INODE_DEBUG,
570                    "pvfs2_get_custom_inode_commmon: setting SGID bit.\n");
571                inode->i_mode = mode | S_ISGID;
572            }
573            else
574            {
575                inode->i_mode = mode;
576            }
577        }
578        gossip_debug(GOSSIP_INODE_DEBUG,
579                "pvfs2_get_custom_inode_common: inode: %p, inode->i_mode %o\n",
580                inode, inode->i_mode);
581        inode->i_mapping->host = inode;
582#ifdef HAVE_CURRENT_FSUID
583        inode->i_uid = current_fsuid();
584        inode->i_gid = current_fsgid();
585#else
586        inode->i_uid = current->fsuid;
587        inode->i_gid = current->fsgid;
588#endif
589        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
590        inode->i_size = PAGE_CACHE_SIZE;
591#ifdef HAVE_I_BLKSIZE_IN_STRUCT_INODE
592        inode->i_blksize = PAGE_CACHE_SIZE;
593#endif
594        inode->i_blkbits = PAGE_CACHE_SHIFT;
595        inode->i_blocks = 0;
596        inode->i_rdev = dev;
597        inode->i_bdev = NULL;
598        inode->i_cdev = NULL;
599        inode->i_mapping->a_ops = &pvfs2_address_operations;
600#ifndef PVFS2_LINUX_KERNEL_2_4
601        inode->i_mapping->backing_dev_info = &pvfs2_backing_dev_info;
602#endif
603
604        gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_get_custom_inode: inode %p allocated\n  "
605                    "(pvfs2_inode is %p | sb is %p)\n", inode,
606                    pvfs2_inode, inode->i_sb);
607
608        if ((mode & S_IFMT) == S_IFREG)
609        {
610            inode->i_op = &pvfs2_file_inode_operations;
611            inode->i_fop = &pvfs2_file_operations;
612
613#ifdef HAVE_I_BLKSIZE_IN_STRUCT_INODE
614            inode->i_blksize = pvfs_bufmap_size_query();
615#endif
616            inode->i_blkbits = PAGE_CACHE_SHIFT;
617        }
618        else if ((mode & S_IFMT) == S_IFLNK)
619        {
620            inode->i_op = &pvfs2_symlink_inode_operations;
621            inode->i_fop = NULL;
622        }
623        else if ((mode & S_IFMT) == S_IFDIR)
624        {
625            inode->i_op = &pvfs2_dir_inode_operations;
626            inode->i_fop = &pvfs2_dir_operations;
627
628            /* dir inodes start with i_nlink == 2 (for "." entry) */
629            inode->i_nlink++;
630        }
631        else
632        {
633            gossip_debug(GOSSIP_INODE_DEBUG, "pvfs2_get_custom_inode: unsupported mode\n");
634            goto error;
635        }
636#if !defined(PVFS2_LINUX_KERNEL_2_4) && defined(HAVE_GENERIC_GETXATTR) && defined(CONFIG_FS_POSIX_ACL)
637        gossip_debug(GOSSIP_ACL_DEBUG, "Initializing ACL's for inode %llu\n",
638                llu(get_handle_from_ino(inode)));
639        /* Initialize the ACLs of the new inode */
640        pvfs2_init_acl(inode, dir);
641#endif
642    }
643error:
644    return inode;
645}
646
647/*
648 * Local variables:
649 *  c-indent-level: 4
650 *  c-basic-offset: 4
651 * End:
652 *
653 * vim: ts=8 sts=4 sw=4 expandtab
654 */
Note: See TracBrowser for help on using the browser.