pwd program returns "/././."?

Discuss Programming

pwd program returns "/././."?

Postby ZiaTioN » Thu Feb 19, 2004 7:29 pm

I wrote this C program to emulate the Linux "pwd" function and did not want to use getcwd(). I wanted to actually do it the way I think the pwd binary does it. Anyway here is what I have:

Code: Select all
#include <dirent.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    struct stat stat_buf;   
    struct dirent *file_info;
    ino_t itself_ino;        /* holds current folder inode */
    ino_t parent_ino;        /* holds parent folder inode */
    char Current[PATH_MAX];  /* folder name */
    char Path[PATH_MAX];     /* holds the full path */
    char Slash[PATH_MAX];    /* add / before the folder name */
    DIR *dir;

    while (1)
    {   
        dir = opendir(".");
        if(dir == NULL) {
            fprintf(stderr, "cannot get current directory.\n");
            exit(-1);
        }
        /* read the information about the current folder */
        file_info = readdir(dir);

        lstat(file_info->d_name, &stat_buf);
        itself_ino = stat_buf.st_ino;
        closedir(dir);

        chdir("..");    /* go to parent directory */
        dir = opendir(".");

        file_info = readdir(dir);
        lstat(file_info->d_name, &stat_buf);
        parent_ino = stat_buf.st_ino;

        if(itself_ino == parent_ino) {
            /*closedir(dir);*/
            break;
        } else {
            strcpy(Slash, "/");
            strcpy(Current, file_info->d_name);
            strcat(Slash, Current);  /* add "/" as the first */
            strcat(Slash, Path);     /* charcter of the directory */

            /* check the length of the pathname */
            if(strlen(Slash)  >= PATH_MAX) {
                fprintf(stderr, "Error! Path too long!\n");
                exit(0);
            }           
            /* save the full pathname */       
            strcpy(Path, Slash);
        }
        closedir(dir);
    }

    /* print the full path of the current working directory */
    printf("%s\n", Path);
    return 0;
}


Now it runs fine but instead of returning each dir's actual name in the complete path it returns "." (dots) so the final path would look like "/././." instead of "/home/user/dir". Any ideas why?
ZiaTioN
administrator
administrator
 
Posts: 460
Joined: Tue Apr 08, 2003 3:28 pm

Postby Void Main » Thu Feb 19, 2004 7:48 pm

Why don't you just look at the source for pwd? The pwd binary actually *does* use getcwd(). I don't know what OS you are using but in Red Hat/Fedora pwd is part of the coreutils package so an "apt-get source coreutils" will download and put the source in /usr/src/redhat/SOURCES.

Would you like to continue doing it the way you are doing or do it like pwd actually does it? Or do you want the actual code for "getcwd()"? If so you'll want to grab the source for libc (glibc).
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA

Postby ZiaTioN » Thu Feb 19, 2004 8:03 pm

I want to continue to do it the way I have done it. It is almost done and should be done but this stupid bug is killing me. The logic works great but the dir names are never returned.

Edit:
[root@localhost root]# apt-get source coreutils
Reading Package Lists... Done
Building Dependency Tree... Done
E: You must put some 'source' URIs in your sources.list


What are some good source URL's? Where is the sources.list?
ZiaTioN
administrator
administrator
 
Posts: 460
Joined: Tue Apr 08, 2003 3:28 pm

Postby Void Main » Thu Feb 19, 2004 8:24 pm

The source URLs should have been placed in your sources.list when you installed apt (assuming you installed apt from freshrpms.net). They are also listed on one of my apt pages (depending on what version of RH/Fedora you are running):

http://voidmain.is-a-geek.net/redhat/fe ... _have.html

I'll look at your source now and see if I can spot the bug.
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA

Postby Void Main » Thu Feb 19, 2004 9:07 pm

Of course using getcwd() makes life easy:

Code: Select all
#include <stdlib.h>
#include <unistd.h>
                                                                               
int main () {
                                                                               
  long size;
  char *buf;
  char *ptr;
                                                                               
  size = pathconf(".", _PC_PATH_MAX);
                                                                               
  if ((buf = (char *)malloc((size_t)size)) != NULL)
      ptr = getcwd(buf, (size_t)size);
                                                                               
  printf("%s\n",ptr);
                                                                               
  return 0;

}


Still looking...
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA

Postby Void Main » Thu Feb 19, 2004 11:53 pm

Ok, I got it working "somewhat":

http://voidmain.is-a-geek.net/files/misc/pwd_broken.c

The problem is the root directory inode number is the same as it is on mounted directories.

e.g.
Code: Select all
[voidmain@weeman /]$ ls -ai
      2 .            15937 etc          143425 misc     318721 sbin
      2 ..               2 home         111553 mnt       79681 tmp
     12 .autofsck        2 initrd       286849 opt      334657 usr
 159361 bin             13 .journal          1 proc     286876 var
  47809 boot        239041 lib          302785 public    47818 workspace
 175297 dev             11 lost+found    95617 root


Notice the inode on / is the same as it is on /home because /home is a file system mounted on the / file system so when I am in /home/voidmain and run the pwd program it outputs "/voidmain". If I am in /home/voidmain/somedir it will output "/voidmain/somedir", etc. Not sure where to go from there without looking at the actual getcwd source code (haven't done that yet). It works great if I run it in any directory actually on the root file system. For example if I am in "/usr/bin" it will output "/usr/bin". It's late, let me know if you come up with anything more.
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA

Postby ZiaTioN » Fri Feb 20, 2004 12:27 pm

Ok now this is strange. Here is the source for getcwd and it seems rather smalll.

Code: Select all
/* Provide a replacement for the POSIX getcwd function.
   Copyright (C) 2003 Free Software Foundation, Inc.
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
/* written by Jim Meyering */
 
#include <config.h>
 
#include <stdlib.h>
#include <string.h>
 
#include <errno.h>
#ifndef errno
extern int errno;
#endif
 
#include <sys/types.h>
 
#include "pathmax.h"
#include "same.h"
 
/* Guess high, because that makes the test below more conservative.
   But this is a kludge, because we should really use
   pathconf (".", _PC_NAME_MAX).  But it's probably not worth the cost.  */
#define KLUDGE_POSIX_NAME_MAX 255
 
#define MAX_SAFE_LEN (PATH_MAX - 1 - KLUDGE_POSIX_NAME_MAX - 1)
 
/* Undefine getcwd here, as near the use as possible, in case any
   of the files included above define it to rpl_getcwd.  */
#undef getcwd
 
/* Any declaration of getcwd from headers included above has
   been changed to a declaration of rpl_getcwd.  Declare it here.  */
extern char *getcwd (char *buf, size_t size);
 
/* This is a wrapper for getcwd.
   Some implementations (at least GNU libc 2.3.1 + linux-2.4.20) return
   non-NULL for a working directory name longer than PATH_MAX, yet the
   returned string is a strict prefix of the desired directory name.
   Upon such a failure, free the offending string, set errno to
   ENAMETOOLONG, and return NULL.
 
   I've heard that this is a Linux kernel bug, and that it has
   been fixed between 2.4.21-pre3 and 2.4.21-pre4.  */
 
char *
rpl_getcwd (char *buf, size_t size)
{
  char *cwd = getcwd (buf, size);
 
  if (cwd == NULL)
    return NULL;
 
  if (strlen (cwd) <= MAX_SAFE_LEN || same_name (cwd, "."))
    return cwd;
 
  free (cwd);
  errno = ENAMETOOLONG;
  return NULL;
}


like 80% of the file is comments. LOL.. Is this saying that this getcwd is merely a wrapper for the POSIX getcwd()? If this is the case where would the source be for this?
ZiaTioN
administrator
administrator
 
Posts: 460
Joined: Tue Apr 08, 2003 3:28 pm

Postby Void Main » Fri Feb 20, 2004 1:55 pm

Yeah, that's not the source for getcwd(), but it does call getcwd() which is why it is so small. I found that one last night as well. Did you try my modifications? The changes I made actually makes your code work except for on the mounted dirs. I'll look for the getcwd source tonight and see what I can come up with. You should be able to track through the included headers and find out more.
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA

Postby ZiaTioN » Fri Feb 20, 2004 2:19 pm

Yeah I did make the changes and it does the same as yours did. As to say it works great on paths not directly mounted to the / dir.

One of the main changes was the while loop you added while reading dirs. I had this before but took it out to try and optimize the code but forgot I needed it to traverse each file (or dir) in the current dir. The thing about the iinode number being the same on mounted drives as the / dir is going to be somewhat difficult to overcome (but I know it is possible!). :)
ZiaTioN
administrator
administrator
 
Posts: 460
Joined: Tue Apr 08, 2003 3:28 pm

Postby Void Main » Fri Feb 20, 2004 2:59 pm

The Linux way might be a little more complicated. I did a little searching through my kernel source and believe I have found the answer. The same file on the web:

http://www.iglu.org.il/lxr/source/fs/dcache.c

Look for the "sys_getcwd()" function as well as read the comment above it.
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA

Postby Void Main » Fri Feb 20, 2004 6:43 pm

Found the source for getcwd in the glibc source (apt-get source glibc). I extracted /usr/src/redhat/SOURCES/glibc-2.3.2-200310271512.tar.bz2 and did a find in that directory for getcwd.c and got this:

Code: Select all
# find | grep getcwd.c
./io/tst-getcwd.c
./sysdeps/generic/getcwd.c
./sysdeps/mach/hurd/getcwd.c
./sysdeps/posix/getcwd.c
./sysdeps/unix/sysv/linux/getcwd.c


Here is the ./sysdeps/posix/getcwd.c:

http://voidmain.is-a-geek.net/files/source/getcwd.c

I hacked it up (changed the getcwd() function name to void_getcwd(), added a couple of headers and removed the references to the external function calls, and added a main() at the bottom) here:

http://voidmain.is-a-geek.net/files/source/void_pwd.c

Do a "make void_pwd" and "./void_pwd" (for those that may be following along) and it should act much like the "pwd" command (well, it doesn't accept params such as --help, etc).

If you wanted to see the exact changes you can look at the diff/patch here:

http://voidmain.is-a-geek.net/files/sou ... wd.c.patch

Of course the old lines start with a "-" and the new lines start with a "+".

Notice how they determine whether the directory is a mount point (solves the inode problem I ran into):

Code: Select all
      /* Figure out if this directory is a mount point.  */
      if (lstat (dotp, &st) < 0)
   goto lose;
      dotdev = st.st_dev;
      dotino = st.st_ino;
      mount_point = dotdev != thisdev;


I still have a hard time when I see the liberal use of the "goto" statement. Heck, I even hate using the "break" statement to break out of loops. But you see a lot of goto statements in the kernel and lower level libraries. They can be very useful in certain situations. I think they normally use them when they are trying to get things to translate down to the fewest assembly instructions for maximum speed and minimal size.

Have fun!
User avatar
Void Main
Site Admin
Site Admin
 
Posts: 5705
Joined: Wed Jan 08, 2003 5:24 am
Location: Tuxville, USA


Return to Programming

Who is online

Users browsing this forum: No registered users and 0 guests

cron